diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000000..b32f7686301 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1 @@ +Checks: '-clang-analyzer-security.insecureAPI.rand' diff --git a/.travis.yml b/.travis.yml index a17163a3e6c..4948faf39c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -95,6 +95,12 @@ matrix: addons: apt: packages: [ 'lib32stdc++6' ] + - os: linux + env: FLAVOR=linux CXX=clang++-3.8 BUILDTYPE=Release ACTION=tidy AWS_ACCESS_KEY_ID= + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise' ] + packages: [ 'clang-tidy-3.8', 'libgcc-4.9-dev', 'libstdc++-4.9-dev', 'libstdc++6', 'libllvm3.4', 'libclang-common-3.8-dev', 'libclang1-3.8', 'liblldb-3.8', 'libllvm3.8', 'lldb-3.8', 'llvm-3.8', 'llvm-3.8-dev', 'llvm-3.8-runtime', 'xutils-dev', 'libxxf86vm-dev', 'x11proto-xf86vidmode-dev', 'mesa-utils' ] env: global: @@ -121,7 +127,7 @@ install: - ./scripts/${FLAVOR}/install.sh script: -- ./scripts/${FLAVOR}/run.sh +- ./scripts/${FLAVOR}/${ACTION:-run}.sh after_failure: - "[ -f ./scripts/${FLAVOR}/after_failure.sh ] && ./scripts/${FLAVOR}/after_failure.sh" diff --git a/CHANGELOG.md b/CHANGELOG.md index ace53f9e7b2..5d549c3bdcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Known issues: ## iOS master +- Fixed CoreTelephony.framework crash. ([#3170](https://github.com/mapbox/mapbox-gl-native/pull/3170)) - `MGLMapView` methods that alter the viewport now accept optional completion handlers. ([#3090](https://github.com/mapbox/mapbox-gl-native/pull/3090)) - Fixed an issue preventing the compass from responding to taps after the compass is moved programmatically. ([#3117](https://github.com/mapbox/mapbox-gl-native/pull/3117)) @@ -37,8 +38,11 @@ Known issues: - A new `MGLAnnotationImage.enabled` property allows you to disable touch events on individual annotations. ([#2501](https://github.com/mapbox/mapbox-gl-native/pull/2501)) - Fixed a rendering issue that caused one-way arrows along tile boundaries to point due east instead of in the direction of travel. ([#2530](https://github.com/mapbox/mapbox-gl-native/pull/2530)) - Fixed an issue that prevented zoom level–dependent style properties from updating after zooming programmatically with animation. ([#2951](https://github.com/mapbox/mapbox-gl-native/pull/2951)) +- Performance and appearance improvements during annotation adds & removes. ([#1688](https://github.com/mapbox/mapbox-gl-native/issues/1688)) +- Overall improved performance during renders by not rendering faster than necessary. ([#1975](https://github.com/mapbox/mapbox-gl-native/issues/1975)) - Fixed a rendering issue with styles that use the `background-pattern` property. ([#2531](https://github.com/mapbox/mapbox-gl-native/pull/2531)) - Fixed a crash when reusing a single `MGLMapView` across multiple `UIViewController`s. ([#2969](https://github.com/mapbox/mapbox-gl-native/pull/2969)) +- Fixed a crash on iPod touch and other devices or simulators without a cell carrier. ([#2687](https://github.com/mapbox/mapbox-gl-native/issues/2687)) - Eliminated flickering when opening and closing an overlay, such as an alert or action sheet. ([#2309](https://github.com/mapbox/mapbox-gl-native/pull/2309)) - Labels can now line wrap on hyphens and other punctuation. ([#2598](https://github.com/mapbox/mapbox-gl-native/pull/2598)) - A new delegate callback was added for observing taps to annotation callout views. ([#2596](https://github.com/mapbox/mapbox-gl-native/pull/2596)) diff --git a/LICENSE.md b/LICENSE.md index 58b949436d6..0afb6d34045 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -mapbox-gl-native copyright (c) 2014, Mapbox. +mapbox-gl-native copyright (c) 2014-2015, Mapbox. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/Makefile b/Makefile index 48fdef164da..40dc9be305d 100644 --- a/Makefile +++ b/Makefile @@ -22,9 +22,10 @@ default: ; @printf "You must specify a valid target\n" #### OS X targets ############################################################## ifeq ($(BUILD),osx) -.PHONY: osx xosx run-osx run-xosx +.PHONY: osx xosx nosx run-osx run-xosx osx: ; $(RUN) HOST=osx HOST_VERSION=x86_64 Makefile/osxapp xosx: ; $(RUN) HOST=osx HOST_VERSION=x86_64 Xcode/osxapp +nosx: ; $(RUN) HOST=osx HOST_VERSION=x86_64 Ninja/osxapp run-osx: osx ; @"build/osx-x86_64/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL" run-xosx: xosx ; @"gyp/build/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL" @@ -61,10 +62,18 @@ endif .PHONY: linux run-linux run-valgrind-linux linux: ; $(RUN) Makefile/linuxapp +nlinux: ; $(RUN) Ninja/linuxapp run-linux: linux ; (cd build/linux-x86_64/$(BUILDTYPE) && ./mapbox-gl) run-valgrind-linux: linux (cd build/linux-x86_64/$(BUILDTYPE) && valgrind --leak-check=full --suppressions=../../../scripts/valgrind.sup ./mapbox-gl) + +.PHONY: config compdb tidy +config: ; $(RUN) config +# Generates a compilation database with ninja for use in clang tooling +compdb: ; $(RUN) Ninja/compdb +tidy: ; $(RUN) tidy + .PHONY: android android-lib # Builds a particular android architecture. android-lib-%: ; $(RUN) HOST=android HOST_VERSION=$* Makefile/androidapp diff --git a/android/MapboxGLAndroidSDK/gradle.properties b/android/MapboxGLAndroidSDK/gradle.properties index 68ca0f4c2a1..011273a3c57 100644 --- a/android/MapboxGLAndroidSDK/gradle.properties +++ b/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=2.3.0-SNAPSHOT +VERSION_NAME=2.4.0-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java index 0f9668be8f7..5d4fa91e0b1 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java @@ -18,10 +18,13 @@ /** * A tooltip view */ -final class InfoWindow { +public class InfoWindow { private WeakReference<Marker> mBoundMarker; private WeakReference<MapView> mMapView; + private float mMarkerHeightOffset; + private float mViewWidthOffset; + private PointF mCoordinates; private boolean mIsVisible; protected View mView; @@ -86,22 +89,26 @@ InfoWindow open(Marker boundMarker, LatLng position, int offsetX, int offsetY) { MapView.LayoutParams lp = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT, MapView.LayoutParams.WRAP_CONTENT); mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - // Calculate default Android x,y coordinate - PointF coords = mMapView.get().toScreenLocation(position); - float x = coords.x - (mView.getMeasuredWidth() / 2) + offsetX; - float y = coords.y - mView.getMeasuredHeight() + offsetY; - - // get right/left popup window - float rightSideInfowWindow = x + mView.getMeasuredWidth(); - float leftSideInfoWindow = x; + // Calculate y-offset for update method + mMarkerHeightOffset = -mView.getMeasuredHeight() + offsetY; - // get right/left map view - final float mapRight = mMapView.get().getRight(); - final float mapLeft = mMapView.get().getLeft(); + // Calculate default Android x,y coordinate + mCoordinates = mMapView.get().toScreenLocation(position); + float x = mCoordinates.x - (mView.getMeasuredWidth() / 2) + offsetX; + float y = mCoordinates.y - mView.getMeasuredHeight() + offsetY; if (mView instanceof InfoWindowView) { // only apply repositioning/margin for InfoWindowView Resources resources = mMapView.get().getContext().getResources(); + + // get right/left popup window + float rightSideInfowWindow = x + mView.getMeasuredWidth(); + float leftSideInfoWindow = x; + + // get right/left map view + final float mapRight = mMapView.get().getRight(); + final float mapLeft = mMapView.get().getLeft(); + float marginHorizontal = resources.getDimension(R.dimen.infowindow_margin); float tipViewOffset = resources.getDimension(R.dimen.infowindow_tipview_width) / 2; float tipViewMarginLeft = mView.getMeasuredWidth() / 2 - tipViewOffset; @@ -129,7 +136,6 @@ InfoWindow open(Marker boundMarker, LatLng position, int offsetX, int offsetY) { if (outOfBoundsRight && mapRight - rightSideInfowWindow < marginHorizontal) { x -= marginHorizontal - (mapRight - rightSideInfowWindow); tipViewMarginLeft += marginHorizontal - (mapRight - rightSideInfowWindow) - tipViewOffset; - leftSideInfoWindow = x; } @@ -137,7 +143,6 @@ InfoWindow open(Marker boundMarker, LatLng position, int offsetX, int offsetY) { if (outOfBoundsLeft && leftSideInfoWindow - mapLeft < marginHorizontal) { x += marginHorizontal - (leftSideInfoWindow - mapLeft); tipViewMarginLeft -= (marginHorizontal - (leftSideInfoWindow - mapLeft)) - tipViewOffset; - } // Adjust tipView @@ -149,6 +154,9 @@ InfoWindow open(Marker boundMarker, LatLng position, int offsetX, int offsetY) { mView.setX(x); mView.setY(y); + // Calculate x-offset for update method + mViewWidthOffset = x - mCoordinates.x - offsetX; + close(); //if it was already opened mMapView.get().addView(mView, lp); mIsVisible = true; @@ -196,7 +204,7 @@ void adaptDefaultMarker(Marker overlayItem) { } private void onClose() { - mMapView.get().deselectMarker(); + mMapView.get().deselectMarker(getBoundMarker()); } InfoWindow setBoundMarker(Marker boundMarker) { @@ -226,4 +234,15 @@ private static void setResIds(Context context) { .getIdentifier("id/infowindow_subdescription", null, packageName); mImageId = context.getResources().getIdentifier("id/infowindow_image", null, packageName); } + + public void update() { + MapView mapView = mMapView.get(); + Marker marker = mBoundMarker.get(); + if (mapView != null && marker != null) { + mCoordinates = mapView.toScreenLocation(marker.getPosition()); + mView.setX(mCoordinates.x + mViewWidthOffset); + mView.setY(mCoordinates.y + mMarkerHeightOffset); + } + } + } diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java index 4f43f9bfa2d..ed8e0d98e4c 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java @@ -79,9 +79,9 @@ void setTitle(String title) { /** * Do not use this method. Used internally by the SDK. */ - public void showInfoWindow() { + public InfoWindow showInfoWindow() { if (getMapView() == null) { - return; + return null; } MapView.InfoWindowAdapter infoWindowAdapter = getMapView().getInfoWindowAdapter(); @@ -91,17 +91,18 @@ public void showInfoWindow() { if (content != null) { infoWindow = new InfoWindow(content, getMapView()); showInfoWindow(infoWindow); - return; + return infoWindow; } } getInfoWindow().adaptDefaultMarker(this); - showInfoWindow(getInfoWindow()); + return showInfoWindow(getInfoWindow()); } - private void showInfoWindow(InfoWindow iw) { + private InfoWindow showInfoWindow(InfoWindow iw) { iw.open(this, getPosition(), 0, topOffsetPixels); infoWindowShown = true; + return iw; } private InfoWindow getInfoWindow() { diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeoConstants.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeoConstants.java index 9e6812eb6b2..41ff1802d1d 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeoConstants.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeoConstants.java @@ -1,10 +1,34 @@ package com.mapbox.mapboxsdk.constants; +/** + * GeoConstants exposes constants for doing locational calculations on Earth + */ public class GeoConstants { + + /** + * The equatorial radius value in meters + */ // http://en.wikipedia.org/wiki/Earth_radius#Equatorial_radius public static final int RADIUS_EARTH_METERS = 6378137; + + /** + * The minimum latitude on Earth + */ public static final double MIN_LATITUDE = -85.05112878; + + /** + * The maximum latitude on Earth + */ public static final double MAX_LATITUDE = 85.05112878; + + /** + * The minimum longitude on Earth + */ public static final double MIN_LONGITUDE = -180; + + /** + * The maximum longitude on Earth + */ public static final double MAX_LONGITUDE = 180; + } diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index 3cd542bb17a..33b3d46a632 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -2,12 +2,19 @@ import java.util.Locale; +/** + * MapboxConstants exposes Mapbox related constants + */ public class MapboxConstants { - // Default Locale for data processing (ex: String.toLowerCase(MAPBOX_LOCALE, "foo")) + /** + * Default Locale for data processing (ex: String.toLowerCase(MAPBOX_LOCALE, "foo")) + */ public static final Locale MAPBOX_LOCALE = Locale.US; - // Key used to store access token in AndroidManifest.xml + /** + * Key used to store access token in AndroidManifest.xml + */ public static final String KEY_META_DATA_MANIFEST = "com.mapbox.AccessToken"; } diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MathConstants.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MathConstants.java index a875fada22c..6e320cc9f6b 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MathConstants.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MathConstants.java @@ -1,8 +1,22 @@ package com.mapbox.mapboxsdk.constants; +/** + * MathConstants exposes math related constant values + */ public class MathConstants { + + /** + * Constant used to convert degrees to radials + */ public static final double DEG2RAD = (Math.PI / 180.0); + + /** + * Constant used to convert radials to degrees + */ public static final double RAD2DEG = (180.0 / Math.PI); + /** + * The number PI + */ public static final double PI = Math.PI; } diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java index ce01e7b4c51..a70d37dedc0 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java @@ -2,14 +2,16 @@ import android.support.annotation.IntDef; +import com.mapbox.mapboxsdk.views.MapView; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * MyBearingTracking exposes different types bearing tracking modes. * - * @see - * @see + * @see MapView#setMyBearingTrackingMode(int) + * @see com.mapbox.mapboxsdk.views.UserLocationView#setMyBearingTrackingMode(int) */ public class MyBearingTracking { @@ -21,9 +23,21 @@ public class MyBearingTracking { public @interface Mode { } + /** + * Bearing tracking is disabled + */ public static final int NONE = 0x00000000; + + /** + * Tracking the bearing of the user based on sensor data + */ public static final int COMPASS = 0x00000004; + + /** + * Tracking the bearing of the user based on GPS data + */ public static final int GPS = 0x00000008; -// public static final int COMBINED = 0x00000012; + + //public static final int COMBINED = 0x00000012; } diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java index f477c4ab45d..130ca36f76b 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java @@ -29,7 +29,7 @@ public class MyLocationTracking { public static final int TRACKING_NONE = 0x00000000; /** - * Tracking user, {@link MapView} will reposition to center of {@link com.mapbox.mapboxsdk.views.UserLocationView} + * Tracking the location of the user, {@link MapView} will reposition to center of {@link com.mapbox.mapboxsdk.views.UserLocationView} */ public static final int TRACKING_FOLLOW = 0x00000004; diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidAccessTokenException.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidAccessTokenException.java index af5a8525a5c..889df322aaa 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidAccessTokenException.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidAccessTokenException.java @@ -13,7 +13,7 @@ public class InvalidAccessTokenException extends RuntimeException { public InvalidAccessTokenException() { - super("Using MapView requires setting a valid access token. See the README.md"); + super("Using MapView requires setting a valid access token. See the INSTALL.md"); } } diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java index c1b720cf277..f062d35457a 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java @@ -51,11 +51,12 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; - import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; +import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector; import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.InfoWindow; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; import com.mapbox.mapboxsdk.annotations.Polygon; @@ -84,13 +85,13 @@ import java.util.List; /** - * A {@code MapView} provides an embeddable map interface, similar to the one provided by the Google Maps API. + * A {@code MapView} provides an embeddable map interface. * You use this class to display map information and to manipulate the map contents from your application. * You can center the map on a given coordinate, specify the size of the area you want to display, * and style the features of the map to fit your application's use case. * <p/> * Use of {@code MapView} requires a Mapbox API access token. - * Obtain an access token on the <a href="https://www.mapbox.com/account/apps/">Mapbox account page</a>. + * Obtain an access token on the <a href="https://www.mapbox.com/studio/account/tokens/">Mapbox account page</a>. * <p/> * <strong>Warning:</strong> Please note that you are responsible for getting permission to use the map data, * and for ensuring your use adheres to the relevant terms of use. @@ -114,9 +115,11 @@ public final class MapView extends FrameLayout { private static final String STATE_CENTER_DIRECTION = "centerDirection"; private static final String STATE_ZOOM_LEVEL = "zoomLevel"; private static final String STATE_DIRECTION = "direction"; + private static final String STATE_TILT = "tilt"; private static final String STATE_ZOOM_ENABLED = "zoomEnabled"; private static final String STATE_SCROLL_ENABLED = "scrollEnabled"; private static final String STATE_ROTATE_ENABLED = "rotateEnabled"; + private static final String STATE_TILT_ENABLED = "tiltEnabled"; private static final String STATE_ZOOM_CONTROLS_ENABLED = "zoomControlsEnabled"; private static final String STATE_DEBUG_ACTIVE = "debugActive"; private static final String STATE_STYLE_URL = "styleUrl"; @@ -161,6 +164,14 @@ public final class MapView extends FrameLayout { */ public static final double MAXIMUM_ZOOM_LEVEL = 18.0; + /** + * The currently supported maximum and minimum tilt values. + * + * @see MapView#setTilt(double) + */ + private static final double MINIMUM_TILT = 0; + private static final double MAXIMUM_TILT = 60; + // // Instance members // @@ -178,6 +189,7 @@ public final class MapView extends FrameLayout { private GestureDetectorCompat mGestureDetector; private ScaleGestureDetector mScaleGestureDetector; private RotateGestureDetector mRotateGestureDetector; + private ShoveGestureDetector mShoveGestureDetector; private boolean mTwoTap = false; private boolean mZoomStarted = false; private boolean mQuickZoom = false; @@ -202,7 +214,8 @@ public final class MapView extends FrameLayout { // Every annotation that has been added to the map private final List<Annotation> mAnnotations = new ArrayList<>(); private List<Marker> mMarkersNearLastTap = new ArrayList<>(); - private Marker mSelectedMarker; + private List<Marker> mSelectedMarkers = new ArrayList<>(); + private List<InfoWindow> mInfoWindows = new ArrayList<>(); private InfoWindowAdapter mInfoWindowAdapter; private SpriteFactory mSpriteFactory; private ArrayList<Sprite> mSprites = new ArrayList<>(); @@ -239,6 +252,8 @@ public final class MapView extends FrameLayout { private boolean mZoomEnabled = true; private boolean mScrollEnabled = true; private boolean mRotateEnabled = true; + private boolean mTiltEnabled = true; + private boolean mAllowConcurrentMultipleOpenInfoWindows = false; private String mStyleUrl; // @@ -274,87 +289,115 @@ public final class MapView extends FrameLayout { } /** - * This event is triggered whenever the currently displayed map region is about to changing + * This {@link MapChange} is triggered whenever the currently displayed map region is about to changing * without an animation. * <p/> * This event is followed by a series of {@link MapView#REGION_IS_CHANGING} and ends * with {@link MapView#REGION_DID_CHANGE}. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int REGION_WILL_CHANGE = 0; /** - * This event is triggered whenever the currently displayed map region is about to changing + * This {@link MapChange} is triggered whenever the currently displayed map region is about to changing * with an animation. * <p/> * This event is followed by a series of {@link MapView#REGION_IS_CHANGING} and ends * with {@link MapView#REGION_DID_CHANGE_ANIMATED}. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int REGION_WILL_CHANGE_ANIMATED = 1; /** - * This event is triggered whenever the currently displayed map region is changing. + * This {@link MapChange} is triggered whenever the currently displayed map region is changing. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int REGION_IS_CHANGING = 2; /** - * This event is triggered whenever the currently displayed map region finished changing + * This {@link MapChange} is triggered whenever the currently displayed map region finished changing * without an animation. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int REGION_DID_CHANGE = 3; /** - * This event is triggered whenever the currently displayed map region finished changing + * This {@link MapChange} is triggered whenever the currently displayed map region finished changing * with an animation. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int REGION_DID_CHANGE_ANIMATED = 4; /** - * This event is triggered when the map is about to start loading a new map style. + * This {@link MapChange} is triggered when the map is about to start loading a new map style. * <p/> * This event is followed by {@link MapView#DID_FINISH_LOADING_MAP} or * {@link MapView#DID_FAIL_LOADING_MAP}. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int WILL_START_LOADING_MAP = 5; /** - * This event is triggered when the map has successfully loaded a new map style. + * This {@link MapChange} is triggered when the map has successfully loaded a new map style. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int DID_FINISH_LOADING_MAP = 6; /** - * Currently not implemented. + * This {@link MapChange} is currently not implemented. * <p/> * This event is triggered when the map has failed to load a new map style. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int DID_FAIL_LOADING_MAP = 7; /** - * Currently not implemented. + * This {@link MapChange} is currently not implemented. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int WILL_START_RENDERING_FRAME = 8; /** - * Currently not implemented. + * This {@link MapChange} is currently not implemented. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int DID_FINISH_RENDERING_FRAME = 9; /** - * Currently not implemented. + * This {@link MapChange} is currently not implemented. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int DID_FINISH_RENDERING_FRAME_FULLY_RENDERED = 10; /** - * Currently not implemented. + * This {@link MapChange} is currently not implemented. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int WILL_START_RENDERING_MAP = 11; /** - * Currently not implemented. + * This {@link MapChange} is currently not implemented. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int DID_FINISH_RENDERING_MAP = 12; /** - * Currently not implemented. + * This {@link MapChange} is currently not implemented. + * <p/> + * More information in {@see com.mapbox.mapboxsdk.views.MapView.OnMapChangedListener} */ public static final int DID_FINISH_RENDERING_MAP_FULLY_RENDERED = 13; @@ -468,7 +511,20 @@ public interface OnMapChangedListener { /** * Called when the displayed map view changes. * - * @param change The type of map change event. + * @param change Type of map change event, one of {@link #REGION_WILL_CHANGE}, + * {@link #REGION_WILL_CHANGE_ANIMATED}, + * {@link #REGION_IS_CHANGING}, + * {@link #REGION_DID_CHANGE}, + * {@link #REGION_DID_CHANGE_ANIMATED}, + * {@link #WILL_START_LOADING_MAP}, + * {@link #DID_FAIL_LOADING_MAP}, + * {@link #DID_FINISH_LOADING_MAP}, + * {@link #WILL_START_RENDERING_FRAME}, + * {@link #DID_FINISH_RENDERING_FRAME}, + * {@link #DID_FINISH_RENDERING_FRAME_FULLY_RENDERED}, + * {@link #WILL_START_RENDERING_MAP}, + * {@link #DID_FINISH_RENDERING_MAP}, + * {@link #DID_FINISH_RENDERING_MAP_FULLY_RENDERED}. */ void onMapChanged(@MapChange int change); } @@ -637,6 +693,7 @@ private void initialize(Context context, AttributeSet attrs) { mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); ScaleGestureDetectorCompat.setQuickScaleEnabled(mScaleGestureDetector, true); mRotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); + mShoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); // Shows the zoom controls if (!context.getPackageManager() @@ -682,6 +739,7 @@ private void initialize(Context context, AttributeSet attrs) { setZoomEnabled(typedArray.getBoolean(R.styleable.MapView_zoom_enabled, true)); setScrollEnabled(typedArray.getBoolean(R.styleable.MapView_scroll_enabled, true)); setRotateEnabled(typedArray.getBoolean(R.styleable.MapView_rotate_enabled, true)); + setTiltEnabled(typedArray.getBoolean(R.styleable.MapView_tilt_enabled, true)); setZoomControlsEnabled(typedArray.getBoolean(R.styleable.MapView_zoom_controls_enabled, isZoomControlsEnabled())); setDebugActive(typedArray.getBoolean(R.styleable.MapView_debug_active, false)); if (typedArray.getString(R.styleable.MapView_style_url) != null) { @@ -754,9 +812,11 @@ public void onCreate(@Nullable Bundle savedInstanceState) { setZoomLevel(savedInstanceState.getDouble(STATE_ZOOM_LEVEL)); setDirection(savedInstanceState.getDouble(STATE_CENTER_DIRECTION)); setDirection(savedInstanceState.getDouble(STATE_DIRECTION)); + setTilt(savedInstanceState.getDouble(STATE_TILT), null); setZoomEnabled(savedInstanceState.getBoolean(STATE_ZOOM_ENABLED)); setScrollEnabled(savedInstanceState.getBoolean(STATE_SCROLL_ENABLED)); setRotateEnabled(savedInstanceState.getBoolean(STATE_ROTATE_ENABLED)); + setTiltEnabled(savedInstanceState.getBoolean(STATE_TILT_ENABLED)); setZoomControlsEnabled(savedInstanceState.getBoolean(STATE_ZOOM_CONTROLS_ENABLED)); setDebugActive(savedInstanceState.getBoolean(STATE_DEBUG_ACTIVE)); setStyleUrl(savedInstanceState.getString(STATE_STYLE_URL)); @@ -808,10 +868,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { addOnMapChangedListener(new OnMapChangedListener() { @Override public void onMapChanged(@MapChange int change) { - if ((change == REGION_WILL_CHANGE) || (change == REGION_WILL_CHANGE_ANIMATED)) { - deselectMarker(); - } - if (change == DID_FINISH_LOADING_MAP) { reloadSprites(); reloadMarkers(); @@ -838,9 +894,11 @@ public void onSaveInstanceState(@NonNull Bundle outState) { // need to set zoom level first because of limitation on rotating when zoomed out outState.putDouble(STATE_ZOOM_LEVEL, getZoomLevel()); outState.putDouble(STATE_CENTER_DIRECTION, getDirection()); + outState.putDouble(STATE_TILT, getTilt()); outState.putBoolean(STATE_ZOOM_ENABLED, isZoomEnabled()); outState.putBoolean(STATE_SCROLL_ENABLED, isScrollEnabled()); outState.putBoolean(STATE_ROTATE_ENABLED, isRotateEnabled()); + outState.putBoolean(STATE_TILT_ENABLED, isTiltEnabled()); outState.putBoolean(STATE_ZOOM_CONTROLS_ENABLED, isZoomControlsEnabled()); outState.putBoolean(STATE_DEBUG_ACTIVE, isDebugActive()); outState.putString(STATE_STYLE_URL, getStyleUrl()); @@ -1061,6 +1119,32 @@ public void setScrollEnabled(boolean scrollEnabled) { this.mScrollEnabled = scrollEnabled; } + // + // Pitch / Tilt + // + + /** + * Gets the current Tilt in degrees of the MapView + * @return tilt in degrees + */ + public double getTilt() { + return mNativeMapView.getPitch(); + } + + /** + * Sets the Tilt in degrees of the MapView. + * @param pitch New tilt in degrees + * @param duration Animation time in milliseconds. If null then 0 is used, making the animation immediate. + */ + @FloatRange(from = MINIMUM_TILT, to = MAXIMUM_TILT) + public void setTilt(Double pitch, @Nullable Long duration) { + long actualDuration = 0; + if (duration != null) { + actualDuration = duration; + } + mNativeMapView.setPitch(pitch, actualDuration); + } + // // Rotation // @@ -1295,6 +1379,59 @@ private void zoom(boolean zoomIn, float x, float y) { } } + // + // Tilt + // + + /** + * Returns whether the user may tilt the map. + * + * @return If true, tilting is enabled. + */ + @UiThread + public boolean isTiltEnabled() { + return mTiltEnabled; + } + + /** + * Changes whether the user may tilt the map. + * <p/> + * This setting controls only user interactions with the map. If you set the value to false, + * you may still change the map location programmatically. + * <p/> + * The default value is true. + * + * @param tiltEnabled If true, tilting is enabled. + */ + @UiThread + public void setTiltEnabled(boolean tiltEnabled) { + this.mTiltEnabled = tiltEnabled; + } + + // + // InfoWindows + // + + /** + * Changes whether the map allows concurrent multiple infowindows to be shown. + * + * @param allow If true, map allows concurrent multiple infowindows to be shown. + */ + @UiThread + public void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { + this.mAllowConcurrentMultipleOpenInfoWindows = allow; + } + + /** + * Returns whether the map allows concurrent multiple infowindows to be shown. + * + * @return If true, map allows concurrent multiple infowindows to be shown. + */ + @UiThread + public boolean isAllowConcurrentMultipleOpenInfoWindows() { + return this.mAllowConcurrentMultipleOpenInfoWindows; + } + // // Debug // @@ -1306,7 +1443,7 @@ private void zoom(boolean zoomIn, float x, float y) { */ @UiThread public boolean isDebugActive() { - return mNativeMapView.getDebug() || mNativeMapView.getCollisionDebug(); + return mNativeMapView.getDebug(); } /** @@ -1319,20 +1456,19 @@ public boolean isDebugActive() { @UiThread public void setDebugActive(boolean debugActive) { mNativeMapView.setDebug(debugActive); - mNativeMapView.setCollisionDebug(debugActive); } /** - * Toggles whether the map debug information is shown. + * Cycles through the map debug options. * <p/> - * The value of {@link MapView#isDebugActive()} is toggled. + * The value of {@link MapView#isDebugActive()} reflects whether there are + * any map debug options enabled or disabled. * * @see MapView#isDebugActive() */ @UiThread - public void toggleDebug() { - mNativeMapView.toggleDebug(); - mNativeMapView.toggleCollisionDebug(); + public void cycleDebugOptions() { + mNativeMapView.cycleDebugOptions(); } // True if map has finished loading the view @@ -1357,7 +1493,7 @@ private boolean isFullyLoaded() { * <li>{@code http://...} or {@code https://...}: * retrieves the style over the Internet from any web server.</li> * <li>{@code asset://...}: - * reads the style from the APK {@code asset/} directory. + * reads the style from the APK {@code assets/} directory. * This is used to load a style bundled with your app.</li> * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li> * </ul> @@ -1660,7 +1796,7 @@ private void loadSprite(Sprite sprite) { } float scale = density / DisplayMetrics.DENSITY_DEFAULT; - mNativeMapView.setSprite( + mNativeMapView.addAnnotationIcon( id, (int) (bitmap.getWidth() / scale), (int) (bitmap.getHeight() / scale), @@ -1872,8 +2008,9 @@ public List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsLis /** * Convenience method for removing a Marker from the map. - * + * <p/> * Calls removeAnnotation() internally + * * @param marker Marker to remove */ @UiThread @@ -2006,7 +2143,8 @@ public double getMetersPerPixelAtLatitude(@FloatRange(from = -180, to = 180) dou /** * Selects a marker. The selected marker will have it's info window opened. - * Any other open info windows will be closed. + * Any other open info windows will be closed unless isAllowConcurrentMultipleOpenInfoWindows() + * is true. * <p/> * Selecting an already selected marker will have no effect. * @@ -2019,12 +2157,14 @@ public void selectMarker(@NonNull Marker marker) { return; } - if (marker == mSelectedMarker) { + if (mSelectedMarkers.contains(marker)) { return; } // Need to deselect any currently selected annotation first - deselectMarker(); + if (!isAllowConcurrentMultipleOpenInfoWindows()) { + deselectMarkers(); + } boolean handledDefaultClick = false; if (mOnMarkerClickListener != null) { @@ -2033,26 +2173,46 @@ public void selectMarker(@NonNull Marker marker) { } if (!handledDefaultClick) { - // default behaviour - // Can't do this as InfoWindow will get hidden - //setCenterCoordinate(marker.getPosition(), true); - marker.showInfoWindow(); + // default behaviour show InfoWindow + mInfoWindows.add(marker.showInfoWindow()); } - mSelectedMarker = marker; + mSelectedMarkers.add(marker); } /** - * Deselects any currently selected marker. The selected marker will have it's info window closed. + * Deselects any currently selected marker. All markers will have it's info window closed. */ @UiThread - public void deselectMarker() { - if (mSelectedMarker != null) { - if (mSelectedMarker.isInfoWindowShown()) { - mSelectedMarker.hideInfoWindow(); - mSelectedMarker = null; + public void deselectMarkers() { + if (mSelectedMarkers.isEmpty()) { + return; + } + + for (Marker marker : mSelectedMarkers) { + if (marker.isInfoWindowShown()) { + marker.hideInfoWindow(); } } + + // Removes all selected markers from the list + mSelectedMarkers.clear(); + } + + /** + * Deselects a currently selected marker. The selected marker will have it's info window closed. + */ + @UiThread + public void deselectMarker(@NonNull Marker marker) { + if (!mSelectedMarkers.contains(marker)) { + return; + } + + if (marker.isInfoWindowShown()) { + marker.hideInfoWindow(); + } + + mSelectedMarkers.remove(marker); } // @@ -2130,8 +2290,8 @@ private void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, dou */ @UiThread @Nullable - public Marker getSelectedMarker() { - return mSelectedMarker; + public List<Marker> getSelectedMarkers() { + return mSelectedMarkers; } private void adjustTopOffsetPixels() { @@ -2145,12 +2305,12 @@ private void adjustTopOffsetPixels() { } } - if (mSelectedMarker != null) { - if (mSelectedMarker.isInfoWindowShown()) { - Marker temp = mSelectedMarker; + for (Marker marker : mSelectedMarkers) { + if (marker.isInfoWindowShown()) { + Marker temp = marker; temp.hideInfoWindow(); temp.showInfoWindow(); - mSelectedMarker = temp; + marker = temp; } } } @@ -2231,6 +2391,9 @@ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int h public void onSurfaceTextureUpdated(SurfaceTexture surface) { mCompassView.update(getDirection()); mUserLocationView.update(); + for (InfoWindow infoWindow : mInfoWindows) { + infoWindow.update(); + } } } @@ -2290,11 +2453,13 @@ protected void onVisibilityChanged(@NonNull View changedView, int visibility) { * @see MapView#setZoomEnabled(boolean) * @see MapView#setScrollEnabled(boolean) * @see MapView#setRotateEnabled(boolean) + * @see MapView#setTiltEnabled(boolean) */ public void setAllGesturesEnabled(boolean enabled) { setZoomEnabled(enabled); setScrollEnabled(enabled); setRotateEnabled(enabled); + setTiltEnabled(enabled); } // Called when user touches the screen, all positions are absolute @@ -2309,6 +2474,7 @@ public boolean onTouchEvent(@NonNull MotionEvent event) { // Check two finger gestures first mRotateGestureDetector.onTouchEvent(event); mScaleGestureDetector.onTouchEvent(event); + mShoveGestureDetector.onTouchEvent(event); // Handle two finger tap switch (event.getActionMasked()) { @@ -2330,7 +2496,9 @@ public boolean onTouchEvent(@NonNull MotionEvent event) { // First pointer up long tapInterval = event.getEventTime() - event.getDownTime(); boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout(); - boolean inProgress = mRotateGestureDetector.isInProgress() || mScaleGestureDetector.isInProgress(); + boolean inProgress = mRotateGestureDetector.isInProgress() + || mScaleGestureDetector.isInProgress() + || mShoveGestureDetector.isInProgress(); if (mTwoTap && isTap && !inProgress) { PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event); @@ -2377,13 +2545,12 @@ public boolean onDoubleTapEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: - mQuickZoom = false; break; case MotionEvent.ACTION_MOVE: - mQuickZoom = true; break; case MotionEvent.ACTION_UP: if (mQuickZoom) { + mQuickZoom = false; break; } @@ -2441,19 +2608,24 @@ public boolean onSingleTapConfirmed(MotionEvent e) { Collections.sort(nearbyMarkers); if (nearbyMarkers == mMarkersNearLastTap) { + + // TODO: We still need to adapt this logic to the new mSelectedMarkers list, + // though the basic functionality is there. + // the selection candidates haven't changed; cycle through them - if (mSelectedMarker != null && (mSelectedMarker.getId() == mMarkersNearLastTap.get(mMarkersNearLastTap.size() - 1).getId())) { - // the selected marker is the last in the set; cycle back to the first - // note: this could be the selected marker if only one in set - newSelectedMarkerId = mMarkersNearLastTap.get(0).getId(); - } else if (mSelectedMarker != null) { - // otherwise increment the selection through the candidates - long result = mMarkersNearLastTap.indexOf(mSelectedMarker); - newSelectedMarkerId = mMarkersNearLastTap.get((int) result + 1).getId(); - } else { +// if (mSelectedMarker != null +// && (mSelectedMarker.getId() == mMarkersNearLastTap.get(mMarkersNearLastTap.size() - 1).getId())) { +// // the selected marker is the last in the set; cycle back to the first +// // note: this could be the selected marker if only one in set +// newSelectedMarkerId = mMarkersNearLastTap.get(0).getId(); +// } else if (mSelectedMarker != null) { +// // otherwise increment the selection through the candidates +// long result = mMarkersNearLastTap.indexOf(mSelectedMarker); +// newSelectedMarkerId = mMarkersNearLastTap.get((int) result + 1).getId(); +// } else { // no current selection; select the first one newSelectedMarkerId = mMarkersNearLastTap.get(0).getId(); - } +// } } else { // start tracking a new set of nearby markers mMarkersNearLastTap = nearbyMarkers; @@ -2474,7 +2646,7 @@ public boolean onSingleTapConfirmed(MotionEvent e) { Annotation annotation = mAnnotations.get(i); if (annotation instanceof Marker) { if (annotation.getId() == newSelectedMarkerId) { - if (mSelectedMarker == null || annotation.getId() != mSelectedMarker.getId()) { + if (mSelectedMarkers.isEmpty() || !mSelectedMarkers.contains(annotation)) { selectMarker((Marker) annotation); } break; @@ -2484,7 +2656,7 @@ public boolean onSingleTapConfirmed(MotionEvent e) { } else { // deselect any selected marker - deselectMarker(); + deselectMarkers(); // notify app of map click if (mOnMapClickListener != null) { @@ -2499,7 +2671,7 @@ public boolean onSingleTapConfirmed(MotionEvent e) { // Called for a long press @Override public void onLongPress(MotionEvent e) { - if (mOnMapLongClickListener != null) { + if (mOnMapLongClickListener != null && !mQuickZoom) { LatLng point = fromScreenLocation(new PointF(e.getX(), e.getY())); mOnMapLongClickListener.onMapLongClick(point); } @@ -2556,7 +2728,7 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d } } - // This class handles two finger gestures + // This class handles two finger gestures and double-tap drag gestures private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { long mBeginTime = 0; @@ -2581,8 +2753,8 @@ public void onScaleEnd(ScaleGestureDetector detector) { mZoomStarted = false; } - // Called each time one of the two fingers moves - // Called for pinch zooms + // Called each time a finger moves + // Called for pinch zooms and quickzooms/quickscales @Override public boolean onScale(ScaleGestureDetector detector) { if (!mZoomEnabled) { @@ -2610,6 +2782,9 @@ public boolean onScale(ScaleGestureDetector detector) { // Cancel any animation mNativeMapView.cancelTransitions(); + // Gesture is a quickzoom if there aren't two fingers + mQuickZoom = !mTwoTap; + // Scale the map if (!mQuickZoom && mUserLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE) { // around gesture @@ -2694,6 +2869,71 @@ public boolean onRotate(RotateGestureDetector detector) { } } + // This class handles a vertical two-finger shove. (If you place two fingers on screen with + // less than a 20 degree angle between them, this will detect movement on the Y-axis.) + private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener { + + long mBeginTime = 0; + float mTotalDelta = 0.0f; + boolean mStarted = false; + + @Override + public boolean onShoveBegin(ShoveGestureDetector detector) { + if (!mTiltEnabled) { + return false; + } + + mBeginTime = detector.getEventTime(); + return true; + } + + @Override + public void onShoveEnd(ShoveGestureDetector detector) { + mBeginTime = 0; + mTotalDelta = 0.0f; + mStarted = false; + } + + @Override + public boolean onShove(ShoveGestureDetector detector) { + if (!mTiltEnabled) { + return false; + } + + // If tilt is large enough ignore a tap + // Also if zoom already started, don't tilt + mTotalDelta += detector.getShovePixelsDelta(); + if (!mZoomStarted && ((mTotalDelta > 10.0f) || (mTotalDelta < -10.0f))) { + mStarted = true; + } + + // Ignore short touches in case it is a tap + // Also ignore small tilt + long time = detector.getEventTime(); + long interval = time - mBeginTime; + if (!mStarted && (interval <= ViewConfiguration.getTapTimeout())) { + return false; + } + + if (!mStarted) { + return false; + } + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Get tilt value (scale and clamp) + double pitch = getTilt(); + pitch += 0.1 * detector.getShovePixelsDelta(); + pitch = Math.max(MINIMUM_TILT, Math.min(MAXIMUM_TILT, pitch)); + + // Tilt the map + setTilt(pitch, null); + + return true; + } + } + // This class handles input events from the zoom control buttons // Zoom controls allow single touch only devices to zoom in and out private class OnZoomListener implements ZoomButtonsController.OnZoomListener { @@ -3253,6 +3493,12 @@ public void setOnMyLocationChangeListener(@Nullable OnMyLocationChangeListener l @UiThread public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { mUserLocationView.setMyLocationTrackingMode(myLocationTrackingMode); + validateGesturesForTrackingModes(); + } + + private void validateGesturesForTrackingModes() { + int myLocationTrackingMode = mUserLocationView.getMyLocationTrackingMode(); + int myBearingTrackingMode = mUserLocationView.getMyBearingTrackingMode(); // Enable/disable gestures based on tracking mode if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { @@ -3260,10 +3506,11 @@ public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTra mRotateEnabled = true; } else { mScrollEnabled = false; - mRotateEnabled = (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW); + mRotateEnabled = (myBearingTrackingMode == MyBearingTracking.NONE); } } + /** * Returns the current user location tracking mode. * @@ -3279,7 +3526,12 @@ public int getMyLocationTrackingMode() { /** * Set the current my bearing tracking mode. - * Tracking my bearing disables gestures and shows the direction the user is heading. + * <p/> + * Tracking the users bearing will disable gestures and shows the direction the user is heading. + * <p/> + * When location tracking is disabled the direction of {@link UserLocationView} is rotated + * When location tracking is enabled the {@link MapView} is rotated based on bearing value. + * <p/> * See {@link MyBearingTracking} for different values. * * @param myBearingTrackingMode The bearing tracking mode to be used. @@ -3288,6 +3540,7 @@ public int getMyLocationTrackingMode() { @UiThread public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { mUserLocationView.setMyBearingTrackingMode(myBearingTrackingMode); + validateGesturesForTrackingModes(); } /** diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java index dce90b64d66..f09d0e90273 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java @@ -256,6 +256,14 @@ public void resetPosition() { nativeResetPosition(mNativeMapViewPtr); } + public double getPitch() { + return nativeGetPitch(mNativeMapViewPtr); + } + + public void setPitch(double pitch, long duration) { + nativeSetPitch(mNativeMapViewPtr, pitch, duration); + } + public void scaleBy(double ds) { scaleBy(ds, -1.0, -1.0); } @@ -385,8 +393,8 @@ public long[] getAnnotationsInBounds(BoundingBox bbox) { return nativeGetAnnotationsInBounds(mNativeMapViewPtr, bbox); } - public void setSprite(String symbol, int width, int height, float scale, byte[] pixels) { - nativeSetSprite(mNativeMapViewPtr, symbol, width, height, scale, pixels); + public void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels) { + nativeAddAnnotationIcon(mNativeMapViewPtr, symbol, width, height, scale, pixels); } public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) { @@ -401,7 +409,7 @@ public void setDebug(boolean debug) { nativeSetDebug(mNativeMapViewPtr, debug); } - public void toggleDebug() { + public void cycleDebugOptions() { nativeToggleDebug(mNativeMapViewPtr); } @@ -409,18 +417,6 @@ public boolean getDebug() { return nativeGetDebug(mNativeMapViewPtr); } - public void setCollisionDebug(boolean debug) { - nativeSetCollisionDebug(mNativeMapViewPtr, debug); - } - - public void toggleCollisionDebug() { - nativeToggleCollisionDebug(mNativeMapViewPtr); - } - - public boolean getCollisionDebug() { - return nativeGetCollisionDebug(mNativeMapViewPtr); - } - public boolean isFullyLoaded() { return nativeIsFullyLoaded(mNativeMapViewPtr); } @@ -545,6 +541,10 @@ private native void nativeSetLatLng(long nativeMapViewPtr, LatLng latLng, private native void nativeResetPosition(long nativeMapViewPtr); + private native double nativeGetPitch(long nativeMapViewPtr); + + private native void nativeSetPitch(long nativeMapViewPtr, double pitch, long duration); + private native void nativeScaleBy(long nativeMapViewPtr, double ds, double cx, double cy, long duration); @@ -600,7 +600,7 @@ private native void nativeSetBearing(long nativeMapViewPtr, double degrees, private native long[] nativeGetAnnotationsInBounds(long mNativeMapViewPtr, BoundingBox bbox); - private native void nativeSetSprite(long nativeMapViewPtr, String symbol, + private native void nativeAddAnnotationIcon(long nativeMapViewPtr, String symbol, int width, int height, float scale, byte[] pixels); private native void nativeSetVisibleCoordinateBounds(long mNativeMapViewPtr, LatLng[] coordinates, @@ -614,12 +614,6 @@ private native void nativeSetVisibleCoordinateBounds(long mNativeMapViewPtr, Lat private native boolean nativeGetDebug(long nativeMapViewPtr); - private native void nativeSetCollisionDebug(long nativeMapViewPtr, boolean debug); - - private native void nativeToggleCollisionDebug(long nativeMapViewPtr); - - private native boolean nativeGetCollisionDebug(long nativeMapViewPtr); - private native boolean nativeIsFullyLoaded(long nativeMapViewPtr); private native void nativeSetReachability(long nativeMapViewPtr, boolean status); diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java index b36ac1ab6d0..256330ac2e7 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java @@ -73,7 +73,8 @@ final class UserLocationView extends View { private LatLng mMarkerCoordinate; private ValueAnimator mMarkerCoordinateAnimator; - private float mMarkerDirection; + private float mGpsMarkerDirection; + private float mCompassMarkerDirection; private ObjectAnimator mMarkerDirectionAnimator; private float mMarkerAccuracy; private ObjectAnimator mMarkerAccuracyAnimator; @@ -271,16 +272,27 @@ public void update() { // compute new marker position // TODO add JNI method that takes existing pointf - mMarkerScreenPoint = mMapView.toScreenLocation(mMarkerCoordinate); - mMarkerScreenMatrix.reset(); - mMarkerScreenMatrix.setTranslate( - mMarkerScreenPoint.x, - mMarkerScreenPoint.y); + if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + mMarkerScreenPoint = mMapView.toScreenLocation(mMarkerCoordinate); + mMarkerScreenMatrix.reset(); + mMarkerScreenMatrix.setTranslate( + mMarkerScreenPoint.x, + mMarkerScreenPoint.y); + } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + mMapView.setCenterCoordinate(mMarkerCoordinate, true); + } // rotate so arrow in points to bearing if (mShowDirection) { - mMarkerScreenMatrix.preRotate(mMarkerDirection + - (float) mMapView.getDirection()); + if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) { + mMarkerScreenMatrix.preRotate(mCompassMarkerDirection + (float) mMapView.getDirection()); + } else if (mMyBearingTrackingMode == MyBearingTracking.GPS) { + if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + mMarkerScreenMatrix.preRotate(mGpsMarkerDirection + (float) mMapView.getDirection()); + } else { + mMarkerScreenMatrix.preRotate(mGpsMarkerDirection); + } + } } // adjust accuracy circle @@ -355,9 +367,17 @@ public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTracki mMyBearingTrackingMode = myBearingTrackingMode; if (myBearingTrackingMode == MyBearingTracking.COMPASS) { + mShowDirection = false; mBearingChangeListener.onStart(getContext()); - } else { + } else if (myBearingTrackingMode == MyBearingTracking.GPS) { mBearingChangeListener.onStop(); + if (mUserLocation != null && mUserLocation.hasBearing()) { + mShowDirection = true; + } else { + mShowDirection = false; + } + } else { + mShowDirection = false; } } @@ -535,9 +555,9 @@ previousCoordinate, new LatLng(location) mShowDirection = location.hasBearing(); if (mShowDirection) { if (mUserLocation != null && mUserLocation.hasBearing()) { - mMarkerDirection = mUserLocation.getBearing(); + mGpsMarkerDirection = mUserLocation.getBearing(); } - float oldDir = mMarkerDirection; + float oldDir = mGpsMarkerDirection; float newDir = location.getBearing(); float diff = oldDir - newDir; if (diff > 180.0f) { @@ -550,7 +570,9 @@ previousCoordinate, new LatLng(location) mMarkerDirectionAnimator.start(); } } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW && mMyBearingTrackingMode == MyBearingTracking.GPS) { - // set bearing on map + // always show north & rotate map below + mShowDirection = true; + mGpsMarkerDirection = 0; if (location.hasBearing()) { mMapView.setBearing(mUserLocation.getBearing()); } @@ -579,7 +601,7 @@ private void setCompass(float bearing) { if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { // animate marker mShowDirection = true; - float oldDir = mMarkerDirection; + float oldDir = mCompassMarkerDirection; float newDir = bearing; float diff = oldDir - newDir; if (diff > 180.0f) { @@ -590,12 +612,13 @@ private void setCompass(float bearing) { mMarkerDirectionAnimator = ObjectAnimator.ofFloat(this, "direction", oldDir, newDir); mMarkerDirectionAnimator.setDuration(1000); mMarkerDirectionAnimator.start(); - mMarkerDirection = bearing; + mCompassMarkerDirection = bearing; } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - // change map direction if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) { - mMarkerDirection = bearing; - mMapView.setBearing(mMarkerDirection); + // always show north & change map direction + mShowDirection = true; + mCompassMarkerDirection = 0; + mMapView.setBearing(bearing); } } } @@ -622,12 +645,19 @@ public void setOnMyLocationChangeListener(@Nullable MapView.OnMyLocationChangeLi // public for animator only public float getDirection() { - return mMarkerDirection; + if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) { + return mCompassMarkerDirection; + } + return mGpsMarkerDirection; } // public for animator only public void setDirection(float direction) { - mMarkerDirection = direction % 360.0f; + if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) { + mCompassMarkerDirection = direction % 360.0f; + } else { + mGpsMarkerDirection = direction % 360.0f; + } updateOnNextFrame(); } diff --git a/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml index ad8b600efbe..35696850b7f 100644 --- a/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml +++ b/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml @@ -8,6 +8,7 @@ <attr name="zoom_enabled" format="boolean" /> <attr name="scroll_enabled" format="boolean" /> <attr name="rotate_enabled" format="boolean" /> + <attr name="tilt_enabled" format="boolean" /> <attr name="zoom_controls_enabled" format="boolean" /> <attr name="debug_active" format="boolean" /> <attr name="style_url" format="string" /> diff --git a/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties b/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties index 14c0d73a19b..0031dc8cc81 100644 --- a/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties +++ b/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties @@ -1,3 +1,3 @@ fabric-identifier=com.mapbox.mapboxsdk.mapbox-android-sdk -fabric-version=2.2.0 +fabric-version=2.3.0 fabric-build-type=binary diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index f01181fecaf..de5f3a2aa56 100644 --- a/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -13,7 +13,6 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> - <activity android:name=".MainActivity" android:label="@string/app_name"> @@ -23,39 +22,37 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - <activity android:name=".VisibleCoordinateBoundsActivity" android:label="@string/activity_visible_coordinate_bounds" /> - <activity android:name=".InfoWindowAdapterActivity" android:label="@string/activity_infowindow_adapter" /> - <activity android:name=".InfoWindowActivity" android:label="@string/activity_info_window" /> - + <activity + android:name=".InfoWindowConcurrentActivity" + android:label="@string/activity_info_window_concurrent" /> <activity android:name=".BulkMarkerActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/action_add_bulk_markers" /> - + <activity + android:name=".TiltActivity" + android:label="@string/activity_tilt" /> <activity android:name=".MapFragmentActivity" android:label="@string/activity_map_fragment" /> - <activity android:name=".PressForMarkerActivity" android:label="@string/activity_press_for_marker" /> - - <activity android:name=".ManualZoomActivity" - android:label="@string/action_manual_zoom"/> - + <activity + android:name=".ManualZoomActivity" + android:label="@string/action_manual_zoom" /> <activity android:name=".MyLocationTrackingModeActivity" android:label="@string/activity_user_tracking_mode" /> - <activity android:name=".PolylineActivity" android:label="@string/activity_polyline" /> @@ -63,7 +60,6 @@ <meta-data android:name="io.fabric.ApiKey" android:value="9724157045ff7d083492c6d9ae03e60e8609d461" /> - <meta-data android:name="com.mapbox.AccessToken" android:value="" /> diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivity.java b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivity.java index f53f923b0e4..5764b670174 100644 --- a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivity.java +++ b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivity.java @@ -35,6 +35,9 @@ protected void onCreate(Bundle savedInstanceState) { mMapView.setAccessToken(ApiAccess.getToken(this)); mMapView.onCreate(savedInstanceState); + // Enable to let concurrent multiple infowindows to be shown. + //mMapView.setAllowConcurrentMultipleOpenInfoWindows(true); + mMapView.addMarker(new MarkerOptions() .title("Intersection") .snippet("H St NW with 15th St NW") diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java index e78d3f27365..293893069b6 100644 --- a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java +++ b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java @@ -63,6 +63,9 @@ public View getInfoWindow(@NonNull Marker marker) { } }); + // Enable to let concurrent multiple infowindows to be shown. + //mMapView.setAllowConcurrentMultipleOpenInfoWindows(true); + mMapView.addMarker(generateMarker("Andorra", 42.505777, 1.52529, "#F44336")); mMapView.addMarker(generateMarker("Luxembourg", 49.815273, 6.129583, "#3F51B5")); mMapView.addMarker(generateMarker("Monaco", 43.738418, 7.424616, "#673AB7")); diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivity.java b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivity.java new file mode 100644 index 00000000000..46b3fa05ef2 --- /dev/null +++ b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivity.java @@ -0,0 +1,107 @@ +package com.mapbox.mapboxsdk.testapp; + +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; + +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.utils.ApiAccess; +import com.mapbox.mapboxsdk.views.MapView; + +public class InfoWindowConcurrentActivity extends AppCompatActivity { + + private MapView mMapView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_infowindow_concurrent); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowHomeEnabled(true); + } + + mMapView = (MapView) findViewById(R.id.infoWindowConcurrentMapView); + mMapView.setAccessToken(ApiAccess.getToken(this)); + mMapView.onCreate(savedInstanceState); + + // Enable to let concurrent multiple infowindows to be shown. + mMapView.setAllowConcurrentMultipleOpenInfoWindows(true); + + mMapView.addMarker(new MarkerOptions() + .title("Intersection") + .snippet("H St NW with 15th St NW") + .position(new LatLng(38.9002073, -77.03364419))); + + mMapView.addMarker(new MarkerOptions() + .title("White House") + .snippet("The official residence and principal workplace of the President of the United States, located at 1600 Pennsylvania Avenue NW in Washington, D.C. It has been the residence of every U.S. president since John Adams in 1800.") + .position(new LatLng(38.897705003219784, -77.03655168667463))); + + mMapView.addMarker(new MarkerOptions().title("Intersection") + .snippet("E St NW with 17th St NW") + .position(new LatLng(38.8954236, -77.0394623))); + } + + @Override + protected void onStart() { + super.onStart(); + mMapView.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + mMapView.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mMapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mMapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mMapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mMapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mMapView.onLowMemory(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + +} diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java index 9995a158402..17af8fe2db0 100644 --- a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java +++ b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java @@ -322,8 +322,8 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.action_debug: - // Toggle debug mode - mMapView.toggleDebug(); + // Cycle map debug options + mMapView.cycleDebugOptions(); toggleFpsCounter(mMapView.isDebugActive()); return true; @@ -341,6 +341,10 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { startActivity(new Intent(getApplicationContext(), InfoWindowAdapterActivity.class)); return true; + case R.id.action_tilt: + startActivity(new Intent(getApplicationContext(), TiltActivity.class)); + return true; + case R.id.action_map_fragment: startActivity(new Intent(getApplicationContext(), MapFragmentActivity.class)); return true; @@ -361,6 +365,10 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { startActivity(new Intent(getApplicationContext(), InfoWindowActivity.class)); return true; + case R.id.action_info_window_concurrent: + startActivity(new Intent(getApplicationContext(), InfoWindowConcurrentActivity.class)); + return true; + case R.id.action_visible_bounds: startActivity(new Intent(getApplicationContext(), VisibleCoordinateBoundsActivity.class)); return true; diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/TiltActivity.java b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/TiltActivity.java new file mode 100644 index 00000000000..ff0ad223f49 --- /dev/null +++ b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/TiltActivity.java @@ -0,0 +1,92 @@ +package com.mapbox.mapboxsdk.testapp; + +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.utils.ApiAccess; +import com.mapbox.mapboxsdk.views.MapView; + +public class TiltActivity extends AppCompatActivity { + + private MapView mMapView = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_tilt); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowHomeEnabled(true); + } + + LatLng dc = new LatLng(38.90252, -77.02291); + + // Set up the map + mMapView = (MapView) findViewById(R.id.tiltMapView); + mMapView.setAccessToken(ApiAccess.getToken(this)); + mMapView.setStyleUrl(Style.MAPBOX_STREETS); + mMapView.setCenterCoordinate(dc); + mMapView.setZoomLevel(11); + mMapView.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + mMapView.onStart(); + } + + @Override + protected void onStop() { + super.onStop(); + mMapView.onStop(); + } + + @Override + public void onPause() { + super.onPause(); + mMapView.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + mMapView.onResume(); + + // Tilt Map 45 degrees over 10 seconds + mMapView.setTilt(45.0, 10000l); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mMapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mMapView.onSaveInstanceState(outState); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + +} diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow_concurrent.xml b/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow_concurrent.xml new file mode 100644 index 00000000000..a1d36ac3977 --- /dev/null +++ b/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow_concurrent.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="@color/primary" + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" /> + + <com.mapbox.mapboxsdk.views.MapView + android:id="@+id/infoWindowConcurrentMapView" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:style_url="@string/style_mapbox_streets" + app:center_latitude="38.897705003219784" + app:center_longitude="-77.03655168667463" + app:zoom_level="15" /> + +</LinearLayout> diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_tilt.xml b/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_tilt.xml new file mode 100644 index 00000000000..c211aea5bc1 --- /dev/null +++ b/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_tilt.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="@color/primary" + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" /> + + <com.mapbox.mapboxsdk.views.MapView + android:id="@+id/tiltMapView" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</LinearLayout> diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml b/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml index 8c48894ac9b..f8bf152a5da 100644 --- a/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml +++ b/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml @@ -66,6 +66,12 @@ android:title="@string/navdrawer_menu_title_individual_tests"> <menu> + <item + android:id="@+id/action_tilt" + android:checkable="false" + android:icon="@drawable/ic_flip_to_back_24dp" + android:title="@string/action_tilt" /> + <item android:id="@+id/action_map_fragment" android:checkable="false" @@ -78,6 +84,12 @@ android:icon="@drawable/ic_flip_to_front_24dp" android:title="@string/action_info_window" /> + <item + android:id="@+id/action_info_window_concurrent" + android:checkable="false" + android:icon="@drawable/ic_flip_to_front_24dp" + android:title="@string/action_info_window_concurrent" /> + <item android:id="@+id/action_info_window_adapter" android:checkable="false" diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml b/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml deleted file mode 100644 index 02305f1feb6..00000000000 --- a/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="access_token">null</string> -</resources> \ No newline at end of file diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml index 26b44060eab..2ac09d96b6d 100644 --- a/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml +++ b/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml @@ -4,12 +4,14 @@ <string name="app_name">Mapbox GL</string> <!-- Test Activities --> + <string name="activity_tilt">Map Tilt</string> <string name="activity_infowindow_adapter">InfoWindow Adapter</string> <string name="activity_map_fragment">Map Fragment Activity</string> <string name="activity_press_for_marker">Press For Marker Activity</string> <string name="activity_marker_in_bulk">Add Bulk Markers Activity</string> <string name="activity_manual_zoom">Manual Zoom Activity</string> <string name="activity_info_window">InfoWindow Activity</string> + <string name="activity_info_window_concurrent">InfoWindow Activity (Concurrent)</string> <string name="activity_visible_coordinate_bounds">Visible Coordinate Bounds</string> <string name="activity_user_tracking_mode">User tracking mode</string> <string name="activity_polyline">Polyline Activity</string> @@ -20,13 +22,15 @@ <string name="action_gps">Toggle GPS location</string> <string name="action_user_location_tracking">User location tracking</string> <string name="action_compass">Toggle compass</string> - <string name="action_debug">Toggle debug mode</string> + <string name="action_debug">Cycle map debug options</string> <string name="action_point_annotations">Toggle point annotations</string> <string name="action_info_window_adapter">InfoWindow Adapter</string> + <string name="action_tilt">Tilt</string> <string name="action_map_fragment">MapFragment</string> <string name="action_press_for_marker">Press For Marker</string> <string name="action_manual_zoom">Manual Zoom</string> <string name="action_info_window">InfoWindow</string> + <string name="action_info_window_concurrent">InfoWindow (Concurrent)</string> <string name="action_add_bulk_markers">Add Markers in bulk</string> <string name="action_visible_bounds">Set Visible Bounds</string> <string name="action_visible_bounds_explanation">Center map around 2 markers</string> diff --git a/bin/render.cpp b/bin/render.cpp index ceb95c165cc..71f74b043c5 100644 --- a/bin/render.cpp +++ b/bin/render.cpp @@ -1,10 +1,10 @@ #include <mbgl/map/map.hpp> #include <mbgl/util/image.hpp> #include <mbgl/util/io.hpp> +#include <mbgl/util/run_loop.hpp> #include <mbgl/platform/default/headless_view.hpp> #include <mbgl/platform/default/headless_display.hpp> -#include <mbgl/platform/log.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/sqlite_cache.hpp> @@ -17,18 +17,9 @@ namespace po = boost::program_options; -#include <uv.h> - -#include <cassert> #include <cstdlib> #include <iostream> -#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 - int main(int argc, char *argv[]) { std::string style_path; double lat = 0, lon = 0; @@ -73,8 +64,9 @@ int main(int argc, char *argv[]) { using namespace mbgl; - mbgl::SQLiteCache cache(cache_file); - mbgl::DefaultFileSource fileSource(&cache); + util::RunLoop loop; + SQLiteCache cache(cache_file); + DefaultFileSource fileSource(&cache); // Try to load the token from the environment. if (!token.size()) { @@ -99,19 +91,10 @@ int main(int argc, char *argv[]) { map.setBearing(bearing); if (debug) { - map.setDebug(debug); + map.setDebug(debug ? mbgl::MapDebugOptions::TileBorders | mbgl::MapDebugOptions::ParseStatus : mbgl::MapDebugOptions::NoDebug); } - uv_async_t *async = new uv_async_t; - uv_async_init(uv_default_loop(), async, [](UV_ASYNC_PARAMS(as)) { - std::unique_ptr<PremultipliedImage> image(reinterpret_cast<PremultipliedImage*>(as->data)); - uv_close(reinterpret_cast<uv_handle_t *>(as), [](uv_handle_t *handle) { - delete reinterpret_cast<uv_async_t *>(handle); - }); - util::write_file(output, encodePNG(*image)); - }); - - map.renderStill([async](std::exception_ptr error, PremultipliedImage&& image) { + map.renderStill([&](std::exception_ptr error, PremultipliedImage&& image) { try { if (error) { std::rethrow_exception(error); @@ -121,10 +104,11 @@ int main(int argc, char *argv[]) { exit(1); } - async->data = new PremultipliedImage(std::move(image)); - uv_async_send(async); + util::write_file(output, encodePNG(image)); + loop.stop(); }); - // This loop will terminate once the async was fired. - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); + + return 0; } diff --git a/bin/render.gypi b/bin/render.gypi index c200350a4a7..c01a75f8c34 100644 --- a/bin/render.gypi +++ b/bin/render.gypi @@ -28,16 +28,13 @@ 'variables' : { 'cflags_cc': [ '<@(glfw_cflags)', - '<@(libuv_cflags)', '<@(boost_cflags)', ], 'ldflags': [ '<@(glfw_ldflags)', - '<@(libuv_ldflags)', ], 'libraries': [ '<@(glfw_static_libs)', - '<@(libuv_static_libs)', '<@(boost_libprogram_options_static_libs)' ], }, diff --git a/deps/ninja/ninja-linux b/deps/ninja/ninja-linux new file mode 100755 index 00000000000..e4a6202d999 Binary files /dev/null and b/deps/ninja/ninja-linux differ diff --git a/deps/ninja/ninja-osx b/deps/ninja/ninja-osx new file mode 100755 index 00000000000..64fcacc550c Binary files /dev/null and b/deps/ninja/ninja-osx differ diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000000..ef98a714301 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,16 @@ +FROM ubuntu:12.04 + +ENV DEBIAN_FRONTEND noninteractive + +# Add other APT sources and keys +ADD *.list /etc/apt/sources.list.d/ +ADD *.gpg.key /tmp/ +RUN find /tmp/*.gpg.key | xargs -n1 apt-key add + +# Recreate Travis CI environment +RUN apt-get update -y +RUN apt-get install -y git-core python-pip curl automake libtool make cmake pkg-config python-pip \ + libc6 libstdc++6 zlib1g-dev libcurl4-openssl-dev libpng-dev libsqlite3-dev \ + xvfb libglu1-mesa-dev libxrandr-dev libxinerama-dev libxi-dev libxcursor-dev xutils-dev \ + mesa-utils libxxf86vm-dev x11proto-xf86vidmode-dev && \ + pip install awscli diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 00000000000..c6eb1b110df --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +#!/usr/bin/env bash + +set -e +set -o pipefail + +docker build -t mapbox/gl-native:travis docker diff --git a/docker/clang-tidy/Dockerfile b/docker/clang-tidy/Dockerfile new file mode 100644 index 00000000000..c8803915920 --- /dev/null +++ b/docker/clang-tidy/Dockerfile @@ -0,0 +1,9 @@ +FROM mapbox/gl-native:travis + +# Install compiler +RUN apt-get -y install clang-3.8 lldb-3.8 clang-tidy-3.8 clang-format-3.8 + +RUN useradd -ms /bin/bash mapbox +USER mapbox +ENV HOME /home/mapbox +WORKDIR /home/mapbox diff --git a/docker/clang-tidy/run.sh b/docker/clang-tidy/run.sh new file mode 100755 index 00000000000..421aca9700d --- /dev/null +++ b/docker/clang-tidy/run.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +./docker/build.sh + +docker build -t mapbox/gl-native:clang-tidy docker/clang-tidy + +docker run \ + -i \ + -v `pwd`:/home/mapbox/build \ + -t mapbox/gl-native:clang-tidy \ + build/docker/clang-tidy/tidy.sh diff --git a/docker/clang-tidy/tidy.sh b/docker/clang-tidy/tidy.sh new file mode 100755 index 00000000000..9c939d219b3 --- /dev/null +++ b/docker/clang-tidy/tidy.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# set -e +# set -o pipefail + +export FLAVOR=linux +export CXX=clang++-3.8 +export BUILDTYPE=Release + +cd build + +# before_install +source ./scripts/travis_helper.sh + +# install +./scripts/${FLAVOR}/install.sh + +export CLANG_TIDY=clang-tidy-3.8 +make tidy diff --git a/docker/launchpad.gpg.key b/docker/launchpad.gpg.key new file mode 100644 index 00000000000..ebe1b1ec333 --- /dev/null +++ b/docker/launchpad.gpg.key @@ -0,0 +1,13 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mI0ESuBvRwEEAMi4cDba7xlKaaoXjO1n1HX8RKrkW+HEIl79nSOSJyvzysajs7zU +ow/OzCQp9NswqrDmNuH1+lPTTRNAGtK8r2ouq2rnXT1mTl23dpgHZ9spseR73s4Z +BGw/ag4bpU5dNUStvfmHhIjVCuiSpNn7cyy1JSSvSs3N2mxteKjXLBf7ABEBAAG0 +GkxhdW5jaHBhZCBUb29sY2hhaW4gYnVpbGRziLYEEwECACAFAkrgb0cCGwMGCwkI +BwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAek3eiup7yfzGKA/4xzUqNACSlB+k+DxFF +HqkwKa/ziFiAlkLQyyhm+iqz80htRZr7Ls/ZRYZl0aSU56/hLe0V+TviJ1s8qdN2 +lamkKdXIAFfavA04nOnTzyIBJ82EAUT3Nh45skMxo4z4iZMNmsyaQpNl/m/lNtOL +hR64v5ZybofB2EWkMxUzX8D/FQ== +=LcUQ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/docker/linux/Dockerfile b/docker/linux/Dockerfile index 36eaeed6952..4c97d946e33 100644 --- a/docker/linux/Dockerfile +++ b/docker/linux/Dockerfile @@ -1,14 +1,7 @@ -FROM ubuntu:12.04 +FROM mapbox/gl-native:travis -# Recreate Travis CI environment -RUN apt-get update -y && \ - apt-get install -y build-essential git-core python-pip python-software-properties software-properties-common curl zlib1g-dev automake libtool make cmake pkg-config python-pip libcurl4-openssl-dev libpng-dev libsqlite3-dev xvfb libglu1-mesa-dev libxrandr-dev libxinerama-dev libxi-dev libxcursor-dev imagemagick && \ - pip install awscli - -# Install -RUN add-apt-repository --yes ppa:ubuntu-toolchain-r/test && \ - apt-get update -y && \ - apt-get -y install gdb g++-4.9 gcc-4.9 libllvm3.4 xutils-dev libxxf86vm-dev x11proto-xf86vidmode-dev mesa-utils +# Install compiler +RUN apt-get -y install gdb g++-4.9 gcc-4.9 libllvm3.4 RUN useradd -ms /bin/bash mapbox USER mapbox diff --git a/docker/linux/run-gcc.sh b/docker/linux/run.sh similarity index 91% rename from docker/linux/run-gcc.sh rename to docker/linux/run.sh index 91bbee64ae6..3d41843cf2e 100755 --- a/docker/linux/run-gcc.sh +++ b/docker/linux/run.sh @@ -3,6 +3,8 @@ set -e set -o pipefail +./docker/build.sh + docker build -t mapbox/gl-native:linux docker/linux docker run \ diff --git a/docker/llvm-snapshot.gpg.key b/docker/llvm-snapshot.gpg.key new file mode 100644 index 00000000000..aa6b105aa3d --- /dev/null +++ b/docker/llvm-snapshot.gpg.key @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.12 (GNU/Linux) + +mQINBFE9lCwBEADi0WUAApM/mgHJRU8lVkkw0CHsZNpqaQDNaHefD6Rw3S4LxNmM +EZaOTkhP200XZM8lVdbfUW9xSjA3oPldc1HG26NjbqqCmWpdo2fb+r7VmU2dq3NM +R18ZlKixiLDE6OUfaXWKamZsXb6ITTYmgTO6orQWYrnW6ckYHSeaAkW0wkDAryl2 +B5v8aoFnQ1rFiVEMo4NGzw4UX+MelF7rxaaregmKVTPiqCOSPJ1McC1dHFN533FY +Wh/RVLKWo6npu+owtwYFQW+zyQhKzSIMvNujFRzhIxzxR9Gn87MoLAyfgKEzrbbT +DhqqNXTxS4UMUKCQaO93TzetX/EBrRpJj+vP640yio80h4Dr5pAd7+LnKwgpTDk1 +G88bBXJAcPZnTSKu9I2c6KY4iRNbvRz4i+ZdwwZtdW4nSdl2792L7Sl7Nc44uLL/ +ZqkKDXEBF6lsX5XpABwyK89S/SbHOytXv9o4puv+65Ac5/UShspQTMSKGZgvDauU +cs8kE1U9dPOqVNCYq9Nfwinkf6RxV1k1+gwtclxQuY7UpKXP0hNAXjAiA5KS5Crq +7aaJg9q2F4bub0mNU6n7UI6vXguF2n4SEtzPRk6RP+4TiT3bZUsmr+1ktogyOJCc +Ha8G5VdL+NBIYQthOcieYCBnTeIH7D3Sp6FYQTYtVbKFzmMK+36ERreL/wARAQAB +tD1TeWx2ZXN0cmUgTGVkcnUgLSBEZWJpYW4gTExWTSBwYWNrYWdlcyA8c3lsdmVz +dHJlQGRlYmlhbi5vcmc+iQI4BBMBAgAiBQJRPZQsAhsDBgsJCAcDAgYVCAIJCgsE +FgIDAQIeAQIXgAAKCRAVz00Yr090Ibx+EADArS/hvkDF8juWMXxh17CgR0WZlHCC +9CTBWkg5a0bNN/3bb97cPQt/vIKWjQtkQpav6/5JTVCSx2riL4FHYhH0iuo4iAPR +udC7Cvg8g7bSPrKO6tenQZNvQm+tUmBHgFiMBJi92AjZ/Qn1Shg7p9ITivFxpLyX +wpmnF1OKyI2Kof2rm4BFwfSWuf8Fvh7kDMRLHv+MlnK/7j/BNpKdozXxLcwoFBmn +l0WjpAH3OFF7Pvm1LJdf1DjWKH0Dc3sc6zxtmBR/KHHg6kK4BGQNnFKujcP7TVdv +gMYv84kun14pnwjZcqOtN3UJtcx22880DOQzinoMs3Q4w4o05oIF+sSgHViFpc3W +R0v+RllnH05vKZo+LDzc83DQVrdwliV12eHxrMQ8UYg88zCbF/cHHnlzZWAJgftg +hB08v1BKPgYRUzwJ6VdVqXYcZWEaUJmQAPuAALyZESw94hSo28FAn0/gzEc5uOYx +K+xG/lFwgAGYNb3uGM5m0P6LVTfdg6vDwwOeTNIExVk3KVFXeSQef2ZMkhwA7wya +KJptkb62wBHFE+o9TUdtMCY6qONxMMdwioRE5BYNwAsS1PnRD2+jtlI0DzvKHt7B +MWd8hnoUKhMeZ9TNmo+8CpsAtXZcBho0zPGz/R8NlJhAWpdAZ1CmcPo83EW86Yq7 +BxQUKnNHcwj2ebkCDQRRPZQsARAA4jxYmbTHwmMjqSizlMJYNuGOpIidEdx9zQ5g +zOr431/VfWq4S+VhMDhs15j9lyml0y4ok215VRFwrAREDg6UPMr7ajLmBQGau0Fc +bvZJ90l4NjXp5p0NEE/qOb9UEHT7EGkEhaZ1ekkWFTWCgsy7rRXfZLxB6sk7pzLC +DshyW3zjIakWAnpQ5j5obiDy708pReAuGB94NSyb1HoW/xGsGgvvCw4r0w3xPStw +F1PhmScE6NTBIfLliea3pl8vhKPlCh54Hk7I8QGjo1ETlRP4Qll1ZxHJ8u25f/ta +RES2Aw8Hi7j0EVcZ6MT9JWTI83yUcnUlZPZS2HyeWcUj+8nUC8W4N8An+aNps9l/ +21inIl2TbGo3Yn1JQLnA1YCoGwC34g8QZTJhElEQBN0X29ayWW6OdFx8MDvllbBV +ymmKq2lK1U55mQTfDli7S3vfGz9Gp/oQwZ8bQpOeUkc5hbZszYwP4RX+68xDPfn+ +M9udl+qW9wu+LyePbW6HX90LmkhNkkY2ZzUPRPDHZANU5btaPXc2H7edX4y4maQa +xenqD0lGh9LGz/mps4HEZtCI5CY8o0uCMF3lT0XfXhuLksr7Pxv57yue8LLTItOJ +d9Hmzp9G97SRYYeqU+8lyNXtU2PdrLLq7QHkzrsloG78lCpQcalHGACJzrlUWVP/ +fN3Ht3kAEQEAAYkCHwQYAQIACQUCUT2ULAIbDAAKCRAVz00Yr090IbhWEADbr50X +OEXMIMGRLe+YMjeMX9NG4jxs0jZaWHc/WrGR+CCSUb9r6aPXeLo+45949uEfdSsB +pbaEdNWxF5Vr1CSjuO5siIlgDjmT655voXo67xVpEN4HhMrxugDJfCa6z97P0+ML +PdDxim57uNqkam9XIq9hKQaurxMAECDPmlEXI4QT3eu5qw5/knMzDMZj4Vi6hovL +wvvAeLHO/jsyfIdNmhBGU2RWCEZ9uo/MeerPHtRPfg74g+9PPfP6nyHD2Wes6yGd +oVQwtPNAQD6Cj7EaA2xdZYLJ7/jW6yiPu98FFWP74FN2dlyEA2uVziLsfBrgpS4l +tVOlrO2YzkkqUGrybzbLpj6eeHx+Cd7wcjI8CalsqtL6cG8cUEjtWQUHyTbQWAgG +5VPEgIAVhJ6RTZ26i/G+4J8neKyRs4vz+57UGwY6zI4AB1ZcWGEE3Bf+CDEDgmnP +LSwbnHefK9IljT9XU98PelSryUO/5UPw7leE0akXKB4DtekToO226px1VnGp3Bov +1GBGvpHvL2WizEwdk+nfk8LtrLzej+9FtIcq3uIrYnsac47Pf7p0otcFeTJTjSq3 +krCaoG4Hx0zGQG2ZFpHrSrZTVy6lxvIdfi0beMgY6h78p6M9eYZHQHc02DjFkQXN +bXb5c6gCHESH5PXwPU4jQEE7Ib9J6sbk7ZT2Mw== +=j+4q +-----END PGP PUBLIC KEY BLOCK----- diff --git a/docker/llvm.list b/docker/llvm.list new file mode 100644 index 00000000000..2ca079d9d63 --- /dev/null +++ b/docker/llvm.list @@ -0,0 +1,2 @@ +deb http://llvm.org/apt/precise/ llvm-toolchain-precise main +deb-src http://llvm.org/apt/precise/ llvm-toolchain-precise main diff --git a/docker/ubuntu-toolchain-r.list b/docker/ubuntu-toolchain-r.list new file mode 100644 index 00000000000..2ff8535f958 --- /dev/null +++ b/docker/ubuntu-toolchain-r.list @@ -0,0 +1,2 @@ +deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu precise main +deb-src http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu precise main diff --git a/docs/BUILD_IOS_OSX.md b/docs/BUILD_IOS_OSX.md index 6a430252017..e605ce7ff0e 100644 --- a/docs/BUILD_IOS_OSX.md +++ b/docs/BUILD_IOS_OSX.md @@ -17,7 +17,7 @@ This section is for people contributing to Mapbox GL directly in the context of ### Access Tokens -_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/account/apps/)._ +_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._ Set up the access token by editing the scheme for the application target, then adding an environment variable with the name `MAPBOX_ACCESS_TOKEN`. @@ -83,7 +83,6 @@ Currently, until [#1437](https://github.com/mapbox/mapbox-gl-native/issues/1437) - `libc++.dylib` - `libsqlite3.dylib` - `libz.dylib` - - `CoreTelephony.framework` (optional, telemetry-only) 1. Add `-ObjC` to your target's "Other Linker Flags" build setting (`OTHER_LDFLAGS`). diff --git a/docs/DEVELOP_ANDROID_LINUX.md b/docs/DEVELOP_ANDROID_LINUX.md index 7a918299d66..219bb7d0e94 100644 --- a/docs/DEVELOP_ANDROID_LINUX.md +++ b/docs/DEVELOP_ANDROID_LINUX.md @@ -19,7 +19,7 @@ In the Android SDK Manager also select and install "Android Support Repository" ## Setting Mapbox Access Token -_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/account/apps/)._ +_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._ gradle will take the value of the `MAPBOX_ACCESS_TOKEN` environ variable and save it to `"MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml` where the app will read it from. diff --git a/docs/DEVELOP_ANDROID_OSX.md b/docs/DEVELOP_ANDROID_OSX.md index c98c1e65536..380313aaa86 100644 --- a/docs/DEVELOP_ANDROID_OSX.md +++ b/docs/DEVELOP_ANDROID_OSX.md @@ -17,7 +17,7 @@ By default, the Android SDK will be installed to `/Users/<user>/Library/Android/ ## Setting Mapbox Access Token -_The test application (used for development purposes) uses Mapbox vector tiles, which require a Mapbox account and API access token. Obtain a free access token on the [Mapbox account page](https://www.mapbox.com/account/apps/)._ +_The test application (used for development purposes) uses Mapbox vector tiles, which require a Mapbox account and API access token. Obtain a free access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._ gradle will take the value of the `MAPBOX_ACCESS_TOKEN` environ variable and save it to `MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml` where the app will read it from. diff --git a/docs/DEVELOP_IOS_OSX.md b/docs/DEVELOP_IOS_OSX.md index dbdb43cd0ec..183fd15705a 100644 --- a/docs/DEVELOP_IOS_OSX.md +++ b/docs/DEVELOP_IOS_OSX.md @@ -20,7 +20,7 @@ If you don't have an Apple Developer account, change the destination from "My Ma ### Access Tokens -_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/account/apps/)._ +_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._ Set up the access token by editing the scheme for the application target, then adding an environment variable with the name `MAPBOX_ACCESS_TOKEN`. @@ -46,4 +46,4 @@ If you want to run the tests in Xcode instead, first `make ipackage` to create a - Double-tap to zoom in one level - Two-finger single-tap to zoom out one level - Double-tap, long-pressing the second, then pan up and down to "quick zoom" (iPhone only, meant for one-handed use) -- Use the debug menu to add test annotations, reset position, and toggle debug info. +- Use the debug menu to add test annotations, reset position, and cycle through the debug options. diff --git a/docs/DEVELOP_OSX.md b/docs/DEVELOP_OSX.md index ed9142b0922..02d08657262 100644 --- a/docs/DEVELOP_OSX.md +++ b/docs/DEVELOP_OSX.md @@ -10,7 +10,7 @@ Note that you can't have more than one project in Xcode open at a time because t ### Access Tokens -_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/account/apps/)._ +_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._ Set up the access token by editing the scheme for the application target, then adding an environment variable with the name `MAPBOX_ACCESS_TOKEN`. diff --git a/gyp/asset-fs.gypi b/gyp/asset-fs.gypi index e84fabb629b..a4f3f7f4f00 100644 --- a/gyp/asset-fs.gypi +++ b/gyp/asset-fs.gypi @@ -17,15 +17,8 @@ 'variables': { 'cflags_cc': [ - '<@(libuv_cflags)', '<@(boost_cflags)', ], - 'ldflags': [ - '<@(libuv_ldflags)', - ], - 'libraries': [ - '<@(libuv_static_libs)', - ], 'defines': [ '-DMBGL_ASSET_FS' ], @@ -54,17 +47,6 @@ }] ], }, - - 'link_settings': { - 'conditions': [ - ['OS == "mac"', { - 'libraries': [ '<@(libraries)' ], - 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] } - }, { - 'libraries': [ '<@(libraries)', '<@(ldflags)' ], - }] - ], - }, }, ], } diff --git a/gyp/asset-zip.gypi b/gyp/asset-zip.gypi index e8854734e15..8a19da4fcda 100644 --- a/gyp/asset-zip.gypi +++ b/gyp/asset-zip.gypi @@ -8,7 +8,6 @@ 'sources': [ '../platform/default/asset_request_zip.cpp', - '../platform/default/uv_zip.c', ], 'include_dirs': [ @@ -18,20 +17,15 @@ 'variables': { 'cflags': [ - '<@(libuv_cflags)', '<@(libzip_cflags)', ], 'cflags_cc': [ - '<@(libuv_cflags)', '<@(libzip_cflags)', - '<@(boost_cflags)', ], 'ldflags': [ - '<@(libuv_ldflags)', '<@(libzip_ldflags)', ], 'libraries': [ - '<@(libuv_static_libs)', '<@(libzip_static_libs)', ], 'defines': [ diff --git a/gyp/cache-sqlite.gypi b/gyp/cache-sqlite.gypi index 704dc490eaa..54774a4f051 100644 --- a/gyp/cache-sqlite.gypi +++ b/gyp/cache-sqlite.gypi @@ -19,16 +19,13 @@ 'variables': { 'cflags_cc': [ - '<@(libuv_cflags)', '<@(sqlite_cflags)', ], 'ldflags': [ - '<@(libuv_ldflags)', '<@(sqlite_ldflags)', '<@(zlib_ldflags)', ], 'libraries': [ - '<@(libuv_static_libs)', '<@(sqlite_static_libs)', '<@(zlib_static_libs)', ], diff --git a/gyp/core.gypi b/gyp/core.gypi index a1bf41a0e16..35a958f5563 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -28,7 +28,6 @@ 'variables': { 'cflags_cc': [ - '<@(libuv_cflags)', '<@(opengl_cflags)', '<@(boost_cflags)', '<@(geojsonvt_cflags)', @@ -36,18 +35,15 @@ '<@(rapidjson_cflags)', ], 'cflags': [ - '<@(libuv_cflags)', '<@(opengl_cflags)', '<@(variant_cflags)', '<@(rapidjson_cflags)', '-fPIC' ], 'ldflags': [ - '<@(libuv_ldflags)', '<@(opengl_ldflags)', ], 'libraries': [ - '<@(libuv_static_libs)', '<@(geojsonvt_static_libs)', ], }, diff --git a/gyp/http-android.gypi b/gyp/http-android.gypi index c210db0fb69..84a44adcbcf 100644 --- a/gyp/http-android.gypi +++ b/gyp/http-android.gypi @@ -17,15 +17,8 @@ 'variables': { 'cflags_cc': [ - '<@(libuv_cflags)', '<@(boost_cflags)', ], - 'ldflags': [ - '<@(libuv_ldflags)', - ], - 'libraries': [ - '<@(libuv_static_libs)', - ], 'defines': [ '-DMBGL_HTTP_ANDROID' ], @@ -54,17 +47,6 @@ }] ], }, - - 'link_settings': { - 'conditions': [ - ['OS == "mac"', { - 'libraries': [ '<@(libraries)' ], - 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] } - }, { - 'libraries': [ '<@(libraries)', '<@(ldflags)' ], - }] - ], - }, }, ], } diff --git a/gyp/http-curl.gypi b/gyp/http-curl.gypi index 7ae5b1b75f1..af4c2c5472c 100644 --- a/gyp/http-curl.gypi +++ b/gyp/http-curl.gypi @@ -17,16 +17,13 @@ 'variables': { 'cflags_cc': [ - '<@(libuv_cflags)', '<@(libcurl_cflags)', '<@(boost_cflags)', ], 'ldflags': [ - '<@(libuv_ldflags)', '<@(libcurl_ldflags)', ], 'libraries': [ - '<@(libuv_static_libs)', '<@(libcurl_static_libs)', ], 'defines': [ diff --git a/gyp/http-nsurl.gypi b/gyp/http-nsurl.gypi index efd2c18a7de..af15520c56c 100644 --- a/gyp/http-nsurl.gypi +++ b/gyp/http-nsurl.gypi @@ -16,15 +16,8 @@ ], 'variables': { - 'cflags_cc': [ - '<@(libuv_cflags)', - ], 'ldflags': [ '-framework Foundation', # For NSURLRequest - '<@(libuv_ldflags)', - ], - 'libraries': [ - '<@(libuv_static_libs)', ], 'defines': [ '-DMBGL_HTTP_NSURL' @@ -32,7 +25,6 @@ }, 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ], 'CLANG_ENABLE_OBJC_ARC': 'NO', }, @@ -44,7 +36,6 @@ }, 'link_settings': { - 'libraries': [ '<@(libraries)' ], 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ], }, diff --git a/gyp/platform-android.gypi b/gyp/platform-android.gypi index e3e1141c512..c9a7d3b6a0c 100644 --- a/gyp/platform-android.gypi +++ b/gyp/platform-android.gypi @@ -12,11 +12,14 @@ 'sources': [ '../platform/android/log_android.cpp', '../platform/android/asset_root.cpp', + '../platform/default/async_task.cpp', + '../platform/default/run_loop.cpp', '../platform/default/thread.cpp', '../platform/default/string_stdlib.cpp', '../platform/default/image.cpp', '../platform/default/png_reader.cpp', '../platform/default/jpeg_reader.cpp', + '../platform/default/timer.cpp', ], 'variables': { diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index 205cfe22dd7..a1e00c1d875 100644 --- a/gyp/platform-ios.gypi +++ b/gyp/platform-ios.gypi @@ -10,6 +10,9 @@ ], 'sources': [ + '../platform/default/async_task.cpp', + '../platform/default/run_loop.cpp', + '../platform/default/timer.cpp', '../platform/darwin/log_nslog.mm', '../platform/darwin/string_nsstring.mm', '../platform/darwin/application_root.mm', @@ -51,6 +54,7 @@ '../include/mbgl/ios/MGLShape.h', '../platform/ios/MGLShape.m', '../include/mbgl/ios/MGLAnnotationImage.h', + '../platform/ios/MGLAnnotationImage_Private.h', '../platform/ios/MGLAnnotationImage.m', '../include/mbgl/ios/MGLStyle.h', '../platform/ios/MGLStyle.mm', @@ -88,11 +92,13 @@ '-framework MobileCoreServices', '-framework QuartzCore', '-framework SystemConfiguration', + '-ObjC', ], }, 'include_dirs': [ '../include', + '../src', ], 'xcode_settings': { diff --git a/gyp/platform-linux.gypi b/gyp/platform-linux.gypi index 3ee39e2b70d..eb54bdf9e34 100644 --- a/gyp/platform-linux.gypi +++ b/gyp/platform-linux.gypi @@ -10,14 +10,17 @@ ], 'sources': [ + '../platform/default/async_task.cpp', '../platform/default/log_stderr.cpp', '../platform/default/string_stdlib.cpp', + '../platform/default/run_loop.cpp', '../platform/default/application_root.cpp', '../platform/default/asset_root.cpp', '../platform/default/thread.cpp', '../platform/default/image.cpp', '../platform/default/png_reader.cpp', '../platform/default/jpeg_reader.cpp', + '../platform/default/timer.cpp', ], 'variables': { diff --git a/gyp/platform-osx.gypi b/gyp/platform-osx.gypi index af30019ee52..1ccbd3d59b7 100644 --- a/gyp/platform-osx.gypi +++ b/gyp/platform-osx.gypi @@ -10,6 +10,9 @@ ], 'sources': [ + '../platform/default/async_task.cpp', + '../platform/default/run_loop.cpp', + '../platform/default/timer.cpp', '../platform/darwin/log_nslog.mm', '../platform/darwin/string_nsstring.mm', '../platform/darwin/application_root.mm', diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp index b3e3ab52b6b..00b2446e40d 100644 --- a/include/mbgl/annotation/annotation.hpp +++ b/include/mbgl/annotation/annotation.hpp @@ -9,6 +9,6 @@ namespace mbgl { using AnnotationID = uint32_t; using AnnotationIDs = std::vector<AnnotationID>; -} +} // namespace mbgl #endif diff --git a/include/mbgl/annotation/point_annotation.hpp b/include/mbgl/annotation/point_annotation.hpp index 17b6fe53695..e226673997f 100644 --- a/include/mbgl/annotation/point_annotation.hpp +++ b/include/mbgl/annotation/point_annotation.hpp @@ -17,6 +17,6 @@ class PointAnnotation { const std::string icon; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/annotation/shape_annotation.hpp b/include/mbgl/annotation/shape_annotation.hpp index b220ef50cb8..fffa7dab6e6 100644 --- a/include/mbgl/annotation/shape_annotation.hpp +++ b/include/mbgl/annotation/shape_annotation.hpp @@ -40,6 +40,6 @@ class ShapeAnnotation { const Properties properties; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/ios/MGLAnnotationImage.h b/include/mbgl/ios/MGLAnnotationImage.h index 70bf8b75991..efc79ffb902 100644 --- a/include/mbgl/ios/MGLAnnotationImage.h +++ b/include/mbgl/ios/MGLAnnotationImage.h @@ -13,12 +13,12 @@ NS_ASSUME_NONNULL_BEGIN * @param image The image to be displayed for the annotation. * @param reuseIdentifier The string that identifies that this annotation image is reusable. * @return The initialized annotation image object or `nil` if there was a problem initializing the object. */ -+ (instancetype)annotationImageWithImage:(UIImage *)image reuseIdentifier:(NSString *)reuseIdentifier; ++ (instancetype)annotationImageWithImage:(UIImage *)image reuseIdentifier:(nullable NSString *)reuseIdentifier; /** @name Getting and Setting Attributes */ /** The image to be displayed for the annotation. */ -@property (nonatomic, readonly) UIImage *image; +@property (nonatomic, strong) UIImage *image; /** The string that identifies that this annotation image is reusable. (read-only) * diff --git a/include/mbgl/ios/MGLMapView+IBAdditions.h b/include/mbgl/ios/MGLMapView+IBAdditions.h index 52d5d433b64..f18df56e018 100644 --- a/include/mbgl/ios/MGLMapView+IBAdditions.h +++ b/include/mbgl/ios/MGLMapView+IBAdditions.h @@ -14,12 +14,16 @@ NS_ASSUME_NONNULL_BEGIN // inspectables declared in MGLMapView.h are always sorted before those in // MGLMapView+IBAdditions.h, due to ASCII sort order. +#if TARGET_INTERFACE_BUILDER + // HACK: We want this property to look like a URL bar in the Attributes // inspector, but just calling it styleURL would violate Cocoa naming // conventions and conflict with the existing NSURL property. Fortunately, IB // strips out the two underscores for display. @property (nonatomic, nullable) IBInspectable NSString *styleURL__; +#endif // TARGET_INTERFACE_BUILDER + // Convenience properties related to the initial viewport. These properties // are not meant to be used outside of Interface Builder. latitude and longitude // are backed by properties of type CLLocationDegrees, but these declarations diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index edc3ca53f3b..010b16d5e91 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN /** An MGLMapView object provides an embeddable map interface, similar to the one provided by Apple's MapKit. You use this class to display map information and to manipulate the map contents from your application. You can center the map on a given coordinate, specify the size of the area you want to display, and style the features of the map to fit your application's use case. * -* Use of MGLMapView requires a Mapbox API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/account/apps/). If you instantiate an MGLMapView from Interface Builder, rendering of the map won't begin until the accessToken property has been set. +* Use of MGLMapView requires a Mapbox API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). If you instantiate an MGLMapView from Interface Builder, rendering of the map won't begin until the accessToken property has been set. * * @warning Please note that you are responsible for getting permission to use the map data, and for ensuring your use adheres to the relevant terms of use. */ IB_DESIGNABLE @@ -259,9 +259,6 @@ IB_DESIGNABLE * To display the default style, set this property to `nil`. */ @property (nonatomic, null_resettable) NSURL *styleURL; -/* Discourage programmatic usage of this IB-only property. Interface Builder skips over this declaration because it is unable to parse attributes. See the real declaration in MGLMapView+IBAdditions.h. */ -@property (nonatomic, nullable) IBInspectable NSString *styleURL__ __attribute__((unavailable("styleURL__ is for use within Interface Builder only. Use styleURL in code."))); - /** Currently active style classes, represented as an array of string identifiers. */ @property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses; @@ -405,8 +402,8 @@ IB_DESIGNABLE * The default value of this property is `NO`. */ @property (nonatomic, getter=isDebugActive) BOOL debugActive; -/** Toggle the current value of debugActive. */ -- (void)toggleDebug; +/** Cycle map debug options. */ +- (void)cycleDebugOptions; /** Empties the in-memory tile cache. */ - (void)emptyMemoryCache; diff --git a/include/mbgl/map/camera.hpp b/include/mbgl/map/camera.hpp index d787a39e111..18e7dbf96c1 100644 --- a/include/mbgl/map/camera.hpp +++ b/include/mbgl/map/camera.hpp @@ -22,6 +22,6 @@ struct CameraOptions { std::function<void()> transitionFinishFn; }; -} +} // namespace mbgl #endif /* MBGL_MAP_CAMERA */ diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 35895660f2b..ad13d24eb6c 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -30,7 +30,7 @@ struct CameraOptions; namespace util { template <class T> class Thread; -} +} // namespace util struct EdgeInsets { double top = 0; @@ -146,6 +146,10 @@ class Map : private util::noncopyable { LatLng latLngForPixel(const PrecisionPoint&) const; // Annotations + void addAnnotationIcon(const std::string&, std::shared_ptr<const SpriteImage>); + void removeAnnotationIcon(const std::string&); + double getTopOffsetPixelsForAnnotationIcon(const std::string&); + AnnotationID addPointAnnotation(const PointAnnotation&); AnnotationIDs addPointAnnotations(const std::vector<PointAnnotation>&); @@ -157,31 +161,24 @@ class Map : private util::noncopyable { AnnotationIDs getPointAnnotationsInBounds(const LatLngBounds&); LatLngBounds getBoundsForAnnotations(const AnnotationIDs&); - double getTopOffsetPixelsForAnnotationSymbol(const std::string&); - - // Sprites - void setSprite(const std::string&, std::shared_ptr<const SpriteImage>); - void removeSprite(const std::string&); // Memory void setSourceTileCacheSize(size_t); void onLowMemory(); // Debug - void setDebug(bool value); - void toggleDebug(); - bool getDebug() const; - void setCollisionDebug(bool value); - void toggleCollisionDebug(); - bool getCollisionDebug() const; + void setDebug(MapDebugOptions); + void cycleDebugOptions(); + MapDebugOptions getDebug() const; + bool isFullyLoaded() const; void dumpDebugLogs() const; private: View& view; const std::unique_ptr<Transform> transform; - const std::unique_ptr<MapData> data; const std::unique_ptr<util::Thread<MapContext>> context; + MapData* data; enum class RenderState { never, @@ -192,6 +189,6 @@ class Map : private util::noncopyable { RenderState renderState = RenderState::never; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp index 8b65baf99f9..c197b285893 100644 --- a/include/mbgl/map/mode.hpp +++ b/include/mbgl/map/mode.hpp @@ -5,7 +5,9 @@ namespace mbgl { -enum class MapMode : uint8_t { +using EnumType = uint32_t; + +enum class MapMode : EnumType { Continuous, // continually updating map Still, // a once-off still image }; @@ -14,18 +16,39 @@ enum class MapMode : uint8_t { // being shared. In a shared GL context case, we need to make sure that the // correct GL configurations are in use - they might have changed between render // calls. -enum class GLContextMode : uint8_t { +enum class GLContextMode : EnumType { Unique, Shared, }; // We can choose to constrain the map both horizontally or vertically, or only // vertically e.g. while panning. -enum class ConstrainMode : uint8_t { +enum class ConstrainMode : EnumType { HeightOnly, WidthAndHeight, }; +enum class MapDebugOptions : EnumType { + NoDebug = 0, + TileBorders = 1 << 1, + ParseStatus = 1 << 2, + Timestamps = 1 << 3, + Collision = 1 << 4, +}; + +inline MapDebugOptions operator| (const MapDebugOptions& lhs, const MapDebugOptions& rhs) { + return MapDebugOptions(static_cast<EnumType>(lhs) | static_cast<EnumType>(rhs)); +} + +inline MapDebugOptions& operator|=(MapDebugOptions& lhs, const MapDebugOptions& rhs) { + lhs = lhs | rhs; + return lhs; +} + +inline bool operator& (const MapDebugOptions& lhs, const MapDebugOptions& rhs) { + return static_cast<EnumType>(lhs) & static_cast<EnumType>(rhs); +} + } // namespace mbgl #endif // MBGL_MAP_MODE diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp index ecdd93377f3..6f481c44585 100644 --- a/include/mbgl/map/view.hpp +++ b/include/mbgl/map/view.hpp @@ -80,6 +80,6 @@ class View { protected: mbgl::Map *map = nullptr; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp b/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp index 615320ee552..6364f249dd6 100644 --- a/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp +++ b/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp @@ -22,7 +22,7 @@ class Settings_NSUserDefaults { MGLUserTrackingMode userTrackingMode = MGLUserTrackingModeNone; bool showsUserLocation = false; - bool debug = false; + uint32_t debug = 0; }; } diff --git a/include/mbgl/platform/default/headless_display.hpp b/include/mbgl/platform/default/headless_display.hpp index bf67e3c3b37..4b160ddc687 100644 --- a/include/mbgl/platform/default/headless_display.hpp +++ b/include/mbgl/platform/default/headless_display.hpp @@ -20,6 +20,6 @@ class HeadlessDisplay { #endif }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp index 4b25b81bb9b..07226b5f9ba 100644 --- a/include/mbgl/platform/default/headless_view.hpp +++ b/include/mbgl/platform/default/headless_view.hpp @@ -76,6 +76,6 @@ class HeadlessView : public View { std::thread::id thread; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/platform/default/settings_json.hpp b/include/mbgl/platform/default/settings_json.hpp index 154a7e3769a..707d05eb184 100644 --- a/include/mbgl/platform/default/settings_json.hpp +++ b/include/mbgl/platform/default/settings_json.hpp @@ -1,6 +1,8 @@ #ifndef MBGL_JSON_SETTINGS #define MBGL_JSON_SETTINGS +#include <mbgl/map/mode.hpp> + namespace mbgl { class Settings_JSON { @@ -17,9 +19,9 @@ class Settings_JSON { double bearing = 0; double pitch = 0; - bool debug = false; + EnumType debug = 0; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp index c80186da27b..5c59dc69c7e 100644 --- a/include/mbgl/platform/event.hpp +++ b/include/mbgl/platform/event.hpp @@ -85,6 +85,6 @@ constexpr EventPermutation disabledEventPermutations[] = { { EventSeverity::Debug, Event::Shader } }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/platform/gl.hpp b/include/mbgl/platform/gl.hpp index 6c1229f6d1a..1e98ea7f3b8 100644 --- a/include/mbgl/platform/gl.hpp +++ b/include/mbgl/platform/gl.hpp @@ -110,8 +110,8 @@ class ExtensionFunction<R (Args...)> : protected ExtensionFunctionBase { using glProc = void (*)(); void InitializeExtensions(glProc (*getProcAddress)(const char *)); -} -} +} // namespace gl +} // namespace mbgl #ifdef GL_ES_VERSION_2_0 #define glClearDepth glClearDepthf diff --git a/include/mbgl/platform/log.hpp b/include/mbgl/platform/log.hpp index 86a8cdbef1d..a2a91199053 100644 --- a/include/mbgl/platform/log.hpp +++ b/include/mbgl/platform/log.hpp @@ -72,6 +72,6 @@ class Log { static void platformRecord(EventSeverity severity, const std::string &msg); }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/platform/platform.hpp b/include/mbgl/platform/platform.hpp index f828af37f42..f2550a3e136 100644 --- a/include/mbgl/platform/platform.hpp +++ b/include/mbgl/platform/platform.hpp @@ -29,7 +29,7 @@ void showDebugImage(std::string name, const char *data, size_t width, size_t hei // Shows an alpha image with the specified dimensions in a named window. void showColorDebugImage(std::string name, const char *data, size_t logical_width, size_t logical_height, size_t width, size_t height); -} -} +} // namespace platform +} // namespace mbgl #endif diff --git a/include/mbgl/sprite/sprite_image.hpp b/include/mbgl/sprite/sprite_image.hpp index f36ec5e286c..c5b063ff7fd 100644 --- a/include/mbgl/sprite/sprite_image.hpp +++ b/include/mbgl/sprite/sprite_image.hpp @@ -35,6 +35,6 @@ class SpriteImage : private util::noncopyable { const bool sdf; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 8d2832b33b6..e669cebf725 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -8,7 +8,7 @@ namespace mbgl { namespace util { template <typename T> class Thread; -} +} // namespace util class DefaultFileSource : public FileSource { public: @@ -29,6 +29,6 @@ class DefaultFileSource : public FileSource { std::string accessToken; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/storage/file_cache.hpp b/include/mbgl/storage/file_cache.hpp index b32bdf67e61..65d6dfbff0d 100644 --- a/include/mbgl/storage/file_cache.hpp +++ b/include/mbgl/storage/file_cache.hpp @@ -23,6 +23,6 @@ class FileCache : private util::noncopyable { virtual void put(const Resource &resource, std::shared_ptr<const Response> response, Hint hint) = 0; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp index 0167eccc08a..8d08315cd9b 100644 --- a/include/mbgl/storage/file_source.hpp +++ b/include/mbgl/storage/file_source.hpp @@ -30,6 +30,6 @@ class FileSource : private util::noncopyable { virtual std::unique_ptr<FileRequest> request(const Resource&, Callback) = 0; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/storage/network_status.hpp b/include/mbgl/storage/network_status.hpp index cac2ae193b1..2a0ab78edda 100644 --- a/include/mbgl/storage/network_status.hpp +++ b/include/mbgl/storage/network_status.hpp @@ -4,22 +4,24 @@ #include <mutex> #include <set> -typedef struct uv_async_s uv_async_t; - namespace mbgl { +namespace util { +class AsyncTask; +} // namespace util + class NetworkStatus { public: static void Reachable(); - static void Subscribe(uv_async_t *async); - static void Unsubscribe(uv_async_t *async); + static void Subscribe(util::AsyncTask* async); + static void Unsubscribe(util::AsyncTask* async); private: static std::mutex mtx; - static std::set<uv_async_t *> observers; + static std::set<util::AsyncTask*> observers; }; -} +} // namespace mbgl -#endif \ No newline at end of file +#endif diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp index 0c2a27e9d13..1bbd90ea378 100644 --- a/include/mbgl/storage/resource.hpp +++ b/include/mbgl/storage/resource.hpp @@ -32,6 +32,6 @@ struct Resource { }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 8ab6170ba2e..852110cb2a6 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -1,6 +1,8 @@ #ifndef MBGL_STORAGE_RESPONSE #define MBGL_STORAGE_RESPONSE +#include <mbgl/util/chrono.hpp> + #include <string> #include <memory> @@ -25,8 +27,8 @@ class Response { // The actual data of the response. This is guaranteed to never be empty. std::shared_ptr<const std::string> data; - int64_t modified = 0; - int64_t expires = 0; + Seconds modified = Seconds::zero(); + Seconds expires = Seconds::zero(); std::string etag; }; diff --git a/include/mbgl/storage/sqlite_cache.hpp b/include/mbgl/storage/sqlite_cache.hpp index 3054b6c73c8..4b51d94920c 100644 --- a/include/mbgl/storage/sqlite_cache.hpp +++ b/include/mbgl/storage/sqlite_cache.hpp @@ -9,7 +9,7 @@ namespace mbgl { namespace util { template <typename T> class Thread; -} +} // namespace util class SQLiteCache : public FileCache { public: @@ -36,6 +36,6 @@ class SharedSQLiteCache : util::noncopyable { static std::weak_ptr<SQLiteCache> masterPtr; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index c3e8470886e..be66aca4855 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -212,7 +212,7 @@ MBGL_DEFINE_ENUM_CLASS(TextTransformTypeClass, TextTransformType, { { TextTransformType::Lowercase, "lowercase" }, }); -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/chrono.hpp b/include/mbgl/util/chrono.hpp index df8c5e62349..ce005485bc5 100644 --- a/include/mbgl/util/chrono.hpp +++ b/include/mbgl/util/chrono.hpp @@ -8,9 +8,40 @@ namespace mbgl { using Clock = std::chrono::steady_clock; using SystemClock = std::chrono::system_clock; +using Seconds = std::chrono::seconds; +using Milliseconds = std::chrono::milliseconds; + using TimePoint = Clock::time_point; using Duration = Clock::duration; +using SystemTimePoint = SystemClock::time_point; +using SystemDuration = SystemClock::duration; + +template <class _Clock, class _Duration> +_Duration toDuration(std::chrono::time_point<_Clock, _Duration> time_point) { + return time_point.time_since_epoch(); +} + +template <class _Duration> +Seconds asSeconds(_Duration duration) { + return std::chrono::duration_cast<Seconds>(duration); +} + +template <class _Clock, class _Duration> +Seconds toSeconds(std::chrono::time_point<_Clock, _Duration> time_point) { + return asSeconds(toDuration<_Clock, _Duration>(time_point)); } +template <class _Duration> +Milliseconds asMilliseconds(_Duration duration) { + return std::chrono::duration_cast<Milliseconds>(duration); +} + +template <class _Clock, class _Duration> +Milliseconds toMilliseconds(std::chrono::time_point<_Clock, _Duration> time_point) { + return asMilliseconds(toDuration<_Clock, _Duration>(time_point)); +} + +} // namespace mbgl + #endif diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp index bb5aad2af39..113a3681284 100644 --- a/include/mbgl/util/constants.hpp +++ b/include/mbgl/util/constants.hpp @@ -17,7 +17,7 @@ extern const double M2PI; extern const double EARTH_RADIUS_M; extern const double LATITUDE_MAX; -} +} // namespace util namespace debug { @@ -32,8 +32,8 @@ extern const bool missingFontFaceWarning; extern const bool glyphWarning; extern const bool shapingWarning; -} +} // namespace debug -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/enum.hpp b/include/mbgl/util/enum.hpp index 3c3e4a5c4ea..5d64ed6718a 100644 --- a/include/mbgl/util/enum.hpp +++ b/include/mbgl/util/enum.hpp @@ -49,7 +49,7 @@ struct Enum { using name = ::mbgl::Enum<type, type##_names, sizeof(type##_names) / sizeof(::mbgl::EnumValue<type>)>; \ inline std::ostream& operator<<(std::ostream& os, type t) { return os << name(t).str(); } -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp index 31fa693f8d9..06098840a84 100644 --- a/include/mbgl/util/exception.hpp +++ b/include/mbgl/util/exception.hpp @@ -46,7 +46,7 @@ struct TileLoadingException : Exception { inline TileLoadingException(const std::string &msg) : Exception(msg) {} }; -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 4112b183b2c..2f0e4f6fdc3 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -100,6 +100,6 @@ struct MetersBounds { } }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/gl_helper.hpp b/include/mbgl/util/gl_helper.hpp index 288fc2c681b..7d104fb80eb 100644 --- a/include/mbgl/util/gl_helper.hpp +++ b/include/mbgl/util/gl_helper.hpp @@ -84,7 +84,7 @@ using PreservePixelZoom = Preserve<std::array<double, 2>, getPixelZoom, setPixel using PreserveRasterPos = Preserve<std::array<double, 4>, getRasterPos, setRasterPos>; #endif -} -} +} // namespace gl +} // namespace mbgl #endif diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp index b8e018f696b..fde42c7c5e4 100644 --- a/include/mbgl/util/image.hpp +++ b/include/mbgl/util/image.hpp @@ -14,7 +14,7 @@ enum ImageAlphaMode { template <ImageAlphaMode Mode> class Image { public: - Image() {} + Image() = default; Image(size_t w, size_t h) : width(w), @@ -36,6 +36,6 @@ using PremultipliedImage = Image<ImageAlphaMode::Premultiplied>; PremultipliedImage decodeImage(const std::string&); std::string encodePNG(const PremultipliedImage&); -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/mat4.hpp b/include/mbgl/util/mat4.hpp index 9c343324000..e5b508e5477 100644 --- a/include/mbgl/util/mat4.hpp +++ b/include/mbgl/util/mat4.hpp @@ -42,7 +42,7 @@ void rotate_z(mat4& out, const mat4& a, double rad); void scale(mat4& out, const mat4& a, double x, double y, double z); void multiply(mat4& out, const mat4& a, const mat4& b); -} -} +} // namespace matrix +} // namespace mbgl #endif diff --git a/include/mbgl/util/math.hpp b/include/mbgl/util/math.hpp index 37b1dd3431b..9be0568e12e 100644 --- a/include/mbgl/util/math.hpp +++ b/include/mbgl/util/math.hpp @@ -130,7 +130,7 @@ T smoothstep(T edge0, T edge1, T x) { // (== number of bits required to store x) uint32_t ceil_log2(uint64_t x); -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/include/mbgl/util/noncopyable.hpp b/include/mbgl/util/noncopyable.hpp index ada701efc6c..1789a36de92 100644 --- a/include/mbgl/util/noncopyable.hpp +++ b/include/mbgl/util/noncopyable.hpp @@ -14,10 +14,11 @@ class noncopyable noncopyable( noncopyable const& ) = delete; noncopyable& operator=(noncopyable const& ) = delete; }; -} +} // namespace non_copyable_ typedef non_copyable_::noncopyable noncopyable; -}} +} // namespace util +} // namespace mbgl #endif diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index aff223826a4..f067db2e1f0 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -50,6 +50,6 @@ class Projection { } }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/ptr.hpp b/include/mbgl/util/ptr.hpp index 6e02f956f36..7e8f8ecc184 100644 --- a/include/mbgl/util/ptr.hpp +++ b/include/mbgl/util/ptr.hpp @@ -23,7 +23,7 @@ class ptr : public ::std::shared_ptr<T> { return ::std::shared_ptr<T>::operator*(); } }; -} -} +} // namespace util +} // namespace mbgl #endif \ No newline at end of file diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index 6113ac2215f..fef00743f76 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -2,9 +2,9 @@ #define MBGL_UTIL_RUN_LOOP #include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/util.hpp> #include <mbgl/util/work_task.hpp> #include <mbgl/util/work_request.hpp> -#include <mbgl/util/uv_detail.hpp> #include <functional> #include <utility> @@ -15,21 +15,36 @@ namespace mbgl { namespace util { +typedef void * LOOP_HANDLE; + class RunLoop : private util::noncopyable { public: - RunLoop(uv_loop_t*); - ~RunLoop(); + enum class Type : uint8_t { + Default, + New, + }; - static RunLoop* Get() { - return current.get(); - } + enum class Event : uint8_t { + None = 0, + Read = 1, + Write = 2, + ReadWrite = Read | Write, + }; - static uv_loop_t* getLoop() { - return current.get()->get(); - } + RunLoop(Type type = Type::Default); + ~RunLoop(); + static RunLoop* Get(); + static LOOP_HANDLE getLoopHandle(); + + void run(); + void runOnce(); void stop(); + // So far only needed by the libcurl backend. + void addWatch(int fd, Event, std::function<void(int, Event)>&& callback); + void removeWatch(int fd); + // Invoke fn(args...) on this RunLoop. template <class Fn, class... Args> void invoke(Fn&& fn, Args&&... args) { @@ -38,8 +53,7 @@ class RunLoop : private util::noncopyable { std::move(fn), std::move(tuple)); - withMutex([&] { queue.push(task); }); - async.send(); + push(task); } // Post the cancellable work fn(args...) to this RunLoop. @@ -55,8 +69,7 @@ class RunLoop : private util::noncopyable { std::move(tuple), flag); - withMutex([&] { queue.push(task); }); - async.send(); + push(task); return std::make_unique<WorkRequest>(task); } @@ -73,7 +86,7 @@ class RunLoop : private util::noncopyable { // because if the request was cancelled, then R might have been destroyed. L2 needs to check // the flag because the request may have been cancelled after L2 was invoked but before it // began executing. - auto after = [flag, current = RunLoop::current.get(), callback1 = std::move(callback)] (auto&&... results1) { + auto after = [flag, current = RunLoop::Get(), callback1 = std::move(callback)] (auto&&... results1) { if (!*flag) { current->invoke([flag, callback2 = std::move(callback1)] (auto&&... results2) { if (!*flag) { @@ -89,20 +102,19 @@ class RunLoop : private util::noncopyable { std::move(tuple), flag); - withMutex([&] { queue.push(task); }); - async.send(); + push(task); return std::make_unique<WorkRequest>(task); } - uv_loop_t* get() { return async.get()->loop; } - private: + MBGL_STORE_THREAD(tid) + template <class F, class P> class Invoker : public WorkTask { public: Invoker(F&& f, P&& p, std::shared_ptr<std::atomic<bool>> canceled_ = nullptr) - : canceled(canceled_), + : canceled(std::move(canceled_)), func(std::move(f)), params(std::move(p)) { } @@ -143,17 +155,31 @@ class RunLoop : private util::noncopyable { using Queue = std::queue<std::shared_ptr<WorkTask>>; - void withMutex(std::function<void()>&&); - void process(); + void push(std::shared_ptr<WorkTask>); + + void withMutex(std::function<void()>&& fn) { + std::lock_guard<std::mutex> lock(mutex); + fn(); + } + + void process() { + Queue queue_; + withMutex([&] { queue_.swap(queue); }); + + while (!queue_.empty()) { + (*(queue_.front()))(); + queue_.pop(); + } + } Queue queue; std::mutex mutex; - uv::async async; - static uv::tls<RunLoop> current; + class Impl; + std::unique_ptr<Impl> impl; }; -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/include/mbgl/util/std.hpp b/include/mbgl/util/std.hpp index 0e2d3346bf4..bab5c1494d8 100644 --- a/include/mbgl/util/std.hpp +++ b/include/mbgl/util/std.hpp @@ -24,7 +24,7 @@ void erase_if(Container &container, Predicate pred) { erase_if(container, container.begin(), container.end(), pred); } -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp index 672ead24437..9e2b2d88192 100644 --- a/include/mbgl/util/string.hpp +++ b/include/mbgl/util/string.hpp @@ -35,7 +35,7 @@ inline std::string sprintf(const std::string &msg, Args... args) { return sprintf<max>(msg.c_str(), args...); } -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/include/mbgl/util/time.hpp b/include/mbgl/util/time.hpp index c2e931b10b6..181c961ab2a 100644 --- a/include/mbgl/util/time.hpp +++ b/include/mbgl/util/time.hpp @@ -1,6 +1,8 @@ #ifndef MBGL_UTIL_TIME #define MBGL_UTIL_TIME +#include <mbgl/util/chrono.hpp> + #include <string> #include <cstdint> #include <ctime> @@ -12,8 +14,11 @@ namespace util { // Returns the RFC1123 formatted date. E.g. "Tue, 04 Nov 2014 02:13:24 GMT" std::string rfc1123(std::time_t time); -} +// YYYY-mm-dd HH:MM:SS e.g. "2015-11-26 16:11:23" +std::string iso8601(std::time_t time); + +} // namespace util -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/unitbezier.hpp b/include/mbgl/util/unitbezier.hpp index 095e15f8098..ce34534d5dd 100644 --- a/include/mbgl/util/unitbezier.hpp +++ b/include/mbgl/util/unitbezier.hpp @@ -115,7 +115,7 @@ struct UnitBezier { double cy; }; -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/include/mbgl/util/utf.hpp b/include/mbgl/util/utf.hpp index d6ba2a1f2fc..3d61609c0de 100644 --- a/include/mbgl/util/utf.hpp +++ b/include/mbgl/util/utf.hpp @@ -19,6 +19,7 @@ class utf8_to_utf32 { } }; -}} +} // namespace util +} // namespace mbgl #endif diff --git a/include/mbgl/util/uv.hpp b/include/mbgl/util/uv.hpp deleted file mode 100644 index 9198b0fd1cf..00000000000 --- a/include/mbgl/util/uv.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef MBGL_UTIL_UV -#define MBGL_UTIL_UV - -#include <string> -#include <memory> - -typedef struct uv_handle_s uv_handle_t; -typedef struct uv_async_s uv_async_t; -typedef struct uv_timer_s uv_timer_t; -typedef struct uv_loop_s uv_loop_t; -typedef struct uv_fs_s uv_fs_t; - -namespace uv { - -class rwlock; -class loop; -class async; -class mutex; -class cond; - -class lock { -public: - lock(mutex &); - lock(const std::unique_ptr<mutex> &); - lock(const lock &) = delete; - lock(lock &&lock); - lock &operator=(const lock &lock) = delete; - lock &operator=(lock &&lock); - ~lock(); -private: - mutex *mtx = nullptr; -}; - -class readlock { -public: - readlock(rwlock &); - readlock(const std::unique_ptr<rwlock> &); - readlock(const readlock &) = delete; - readlock(readlock &&lock); - readlock &operator=(const readlock &lock) = delete; - readlock &operator=(readlock &&lock); - ~readlock(); -private: - rwlock *mtx = nullptr; -}; - -class writelock { -public: - writelock(rwlock &); - writelock(const std::unique_ptr<rwlock> &); - writelock(const writelock &) = delete; - writelock(writelock &&lock); - writelock &operator=(const writelock &lock) = delete; - writelock &operator=(writelock &&lock); - ~writelock(); -private: - rwlock *mtx = nullptr; -}; - -template <class T> -class exclusive { -public: - exclusive(T& val, mutex &mtx) : ptr(&val), lock(mtx) {} - exclusive(T *val, mutex &mtx) : ptr(val), lock(mtx) {} - exclusive(mutex &mtx) : lock(mtx) {} - exclusive(const std::unique_ptr<mutex> &mtx) : lock(mtx) {} - exclusive(const exclusive &) = delete; - exclusive(exclusive &&) = default; - exclusive &operator=(const exclusive &) = delete; - exclusive &operator=(exclusive &&) = default; - - T *operator->() { return ptr; } - const T *operator->() const { return ptr; } - T *operator*() { return ptr; } - const T *operator*() const { return ptr; } - operator T&() { return *ptr; } - operator const T&() const { return *ptr; } - - void operator<<(T& val) { operator<<(&val); } - void operator<<(T *val) { - if (ptr) { - throw std::runtime_error("exclusive<> was assigned before"); - } - ptr = val; - } - -private: - T *ptr = nullptr; - class lock lock; -}; - - - -const char *getFileRequestError(uv_fs_t *req); - -template <typename T> -void close(T *specific) { - uv_close(reinterpret_cast<uv_handle_t *>(specific), [](uv_handle_t *generic) { - delete reinterpret_cast<T *>(generic); - }); -} - -} - -#endif diff --git a/include/mbgl/util/uv_detail.hpp b/include/mbgl/util/uv_detail.hpp deleted file mode 100644 index 86a64d33f21..00000000000 --- a/include/mbgl/util/uv_detail.hpp +++ /dev/null @@ -1,220 +0,0 @@ -#ifndef MBGL_UTIL_UV_DETAIL -#define MBGL_UTIL_UV_DETAIL - -#include <mbgl/util/uv.hpp> -#include <mbgl/util/noncopyable.hpp> - -#include <uv.h> - -// XXX: uv.h will include <bits/termios.h> that will -// polute the namespace by defining "B0" which -// will conflict with boost macros. -#ifdef B0 -#undef B0 -#endif - -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -#define UV_ASYNC_PARAMS(handle) uv_async_t *handle, int -#define UV_TIMER_PARAMS(timer) uv_timer_t *timer, int -#else -#define UV_ASYNC_PARAMS(handle) uv_async_t *handle -#define UV_TIMER_PARAMS(timer) uv_timer_t *timer -#endif - -#include <functional> -#include <cassert> -#include <memory> -#include <string> - -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - -// Add thread local storage to libuv API: -// https://github.com/joyent/libuv/commit/5d2434bf71e47802841bad218d521fa254d1ca2d - -typedef pthread_key_t uv_key_t; - -UV_EXTERN int uv_key_create(uv_key_t* key); -UV_EXTERN void uv_key_delete(uv_key_t* key); -UV_EXTERN void* uv_key_get(uv_key_t* key); -UV_EXTERN void uv_key_set(uv_key_t* key, void* value); - -#endif - - -namespace uv { - -class loop : public mbgl::util::noncopyable { -public: - inline loop() { -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - l = uv_loop_new(); - if (l == nullptr) { -#else - l = new uv_loop_t; - if (uv_loop_init(l) != 0) { -#endif - throw std::runtime_error("failed to initialize loop"); - } - } - - inline ~loop() { -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_loop_delete(l); -#else - uv_loop_close(l); - delete l; -#endif - } - - inline void run() { - uv_run(l, UV_RUN_DEFAULT); - } - - inline uv_loop_t* operator*() { - return l; - } - - inline uv_loop_t* get() { - return l; - } - -private: - uv_loop_t *l = nullptr; -}; - -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* h) { - delete reinterpret_cast<T*>(h); - }); - } - - 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_) - : fn(fn_) { - if (uv_async_init(loop, get(), async_cb) != 0) { - throw std::runtime_error("failed to initialize async"); - } - } - - inline void send() { - if (uv_async_send(get()) != 0) { - throw std::runtime_error("failed to async send"); - } - } - -private: - static void async_cb(UV_ASYNC_PARAMS(handle)) { - reinterpret_cast<async*>(handle->data)->fn(); - } - - 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: - static void timer_cb(UV_TIMER_PARAMS(t)) { - reinterpret_cast<timer*>(t->data)->fn(); - } - - std::function<void ()> fn; -}; - -class mutex : public mbgl::util::noncopyable { -public: - inline mutex() { - if (uv_mutex_init(&mtx) != 0) { - throw std::runtime_error("failed to initialize mutex lock"); - } - } - inline ~mutex() { uv_mutex_destroy(&mtx); } - inline void lock() { uv_mutex_lock(&mtx); } - inline void unlock() { uv_mutex_unlock(&mtx); } -private: - uv_mutex_t mtx; -}; - -class rwlock : public mbgl::util::noncopyable { -public: - inline rwlock() { - if (uv_rwlock_init(&mtx) != 0) { - throw std::runtime_error("failed to initialize read-write lock"); - } - } - inline ~rwlock() { uv_rwlock_destroy(&mtx); } - inline void rdlock() { uv_rwlock_rdlock(&mtx); } - inline void wrlock() { uv_rwlock_wrlock(&mtx); } - inline void rdunlock() { uv_rwlock_rdunlock(&mtx); } - inline void wrunlock() { uv_rwlock_wrunlock(&mtx); } - -private: - uv_rwlock_t mtx; -}; - -template <class T> -class tls : public mbgl::util::noncopyable { -public: - inline tls(T* val) { - tls(); - set(val); - } - inline tls() { - if (uv_key_create(&key) != 0) { - throw std::runtime_error("failed to initialize thread local storage key"); - } - } - inline ~tls() { uv_key_delete(&key); } - inline T* get() { return reinterpret_cast<T*>(uv_key_get(&key)); } - inline void set(T* val) { uv_key_set(&key, val); } - -private: - uv_key_t key; -}; - -} - -#endif diff --git a/include/mbgl/util/vec.hpp b/include/mbgl/util/vec.hpp index 4d2d6f6a744..a59a4162f92 100644 --- a/include/mbgl/util/vec.hpp +++ b/include/mbgl/util/vec.hpp @@ -16,7 +16,7 @@ struct vec2 { T x, y; - inline vec2() {} + inline vec2() = default; template<typename U = T, typename std::enable_if<std::numeric_limits<U>::has_quiet_NaN, int>::type = 0> inline vec2(null) : x(std::numeric_limits<T>::quiet_NaN()), y(std::numeric_limits<T>::quiet_NaN()) {} @@ -24,7 +24,7 @@ struct vec2 { template<typename U = T, typename std::enable_if<!std::numeric_limits<U>::has_quiet_NaN, int>::type = 0> inline vec2(null) : x(std::numeric_limits<T>::min()), y(std::numeric_limits<T>::min()) {} - inline vec2(const vec2& o) : x(o.x), y(o.y) {} + inline vec2(const vec2& o) = default; template<typename U> inline vec2(const U& u) : x(u.x), y(u.y) {} @@ -99,7 +99,7 @@ template <typename T = double> struct vec3 { T x, y, z; - inline vec3() {} + inline vec3() = default; inline vec3(const vec3& o) : x(o.x), y(o.y), z(o.z) {} inline vec3(T x_, T y_, T z_) : x(x_), y(y_), z(z_) {} inline bool operator==(const vec3& rhs) const { @@ -111,7 +111,7 @@ template <typename T = double> struct vec4 { T x, y, z, w; - inline vec4() {} + inline vec4() = default; inline vec4(const vec4& o) : x(o.x), y(o.y), z(o.z), w(o.w) {} inline vec4(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {} inline bool operator==(const vec4& rhs) const { @@ -122,6 +122,6 @@ struct vec4 { using Coordinate = vec2<int16_t>; -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/work_request.hpp b/include/mbgl/util/work_request.hpp index f2aa2bbacc9..bd1d97ee703 100644 --- a/include/mbgl/util/work_request.hpp +++ b/include/mbgl/util/work_request.hpp @@ -19,6 +19,6 @@ class WorkRequest : public util::noncopyable { std::shared_ptr<WorkTask> task; }; -} +} // namespace mbgl #endif diff --git a/include/mbgl/util/work_task.hpp b/include/mbgl/util/work_task.hpp index 2224b211c42..a874625c814 100644 --- a/include/mbgl/util/work_task.hpp +++ b/include/mbgl/util/work_task.hpp @@ -16,6 +16,6 @@ class WorkTask : private util::noncopyable { virtual void cancel() = 0; }; -} +} // namespace mbgl #endif diff --git a/ios/Mapbox-iOS-SDK.podspec b/ios/Mapbox-iOS-SDK.podspec index e8cc62f5600..091dd33ad26 100644 --- a/ios/Mapbox-iOS-SDK.podspec +++ b/ios/Mapbox-iOS-SDK.podspec @@ -1,10 +1,10 @@ Pod::Spec.new do |m| m.name = 'Mapbox-iOS-SDK' - m.version = '3.0.0-pre.7-symbols' + m.version = '3.0.0-symbols' 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.' + m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.' m.homepage = 'https://www.mapbox.com/ios-sdk/' m.license = 'BSD' m.author = { 'Mapbox' => 'mobile@mapbox.com' } diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index 2acfe8c297f..4c73f2f171e 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -132,7 +132,7 @@ - (void)showSettings destructiveButtonTitle:nil otherButtonTitles:@"Reset North", @"Reset Position", - @"Toggle Debug", + @"Cycle debug options", @"Empty Memory", @"Add 100 Points", @"Add 1,000 Points", @@ -156,7 +156,7 @@ - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSIn } else if (buttonIndex == actionSheet.firstOtherButtonIndex + 2) { - [self.mapView toggleDebug]; + [self.mapView cycleDebugOptions]; } else if (buttonIndex == actionSheet.firstOtherButtonIndex + 3) { diff --git a/ios/docs/install_docs.sh b/ios/docs/install_docs.sh index 8d1b8f553e0..43af9432b23 100755 --- a/ios/docs/install_docs.sh +++ b/ios/docs/install_docs.sh @@ -17,7 +17,7 @@ README=/tmp/mbgl/GL-README.md cat ./pod-README.md > ${README} echo >> ${README} echo -n "#" >> ${README} -cat ../../CHANGELOG.md >> ${README} +cat ../../CHANGELOG.md | sed -n "/^## iOS ${DOCS_VERSION}/,/^##/p" | sed '$d' >> ${README} # Copy headers to a temporary location where we can substitute macros that appledoc doesn't understand. cp -r ../../include/mbgl/ios /tmp/mbgl perl \ diff --git a/ios/docs/pod-README.md b/ios/docs/pod-README.md index 1fe3ac4f350..b308153de15 100644 --- a/ios/docs/pod-README.md +++ b/ios/docs/pod-README.md @@ -1,7 +1,9 @@ # Mapbox iOS SDK -An open source OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa bindings. +An open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs. For more information, check out [our online overview](https://www.mapbox.com/ios-sdk/). []() + +See the [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/CHANGELOG.md) online. diff --git a/linux/main.cpp b/linux/main.cpp index 97f400dd410..bb571a00933 100644 --- a/linux/main.cpp +++ b/linux/main.cpp @@ -1,6 +1,5 @@ #include <mbgl/mbgl.hpp> #include <mbgl/util/default_styles.hpp> -#include <mbgl/util/uv.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/platform/default/settings_json.hpp> @@ -132,7 +131,7 @@ int main(int argc, char *argv[]) { map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom); map.setBearing(settings.bearing); map.setPitch(settings.pitch); - map.setDebug(settings.debug); + map.setDebug(mbgl::MapDebugOptions(settings.debug)); } view->setChangeStyleCallback([&map] () { @@ -167,7 +166,7 @@ int main(int argc, char *argv[]) { settings.zoom = map.getZoom(); settings.bearing = map.getBearing(); settings.pitch = map.getPitch(); - settings.debug = map.getDebug(); + settings.debug = mbgl::EnumType(map.getDebug()); if (!skipConfig) { settings.save(); } diff --git a/macosx/main.mm b/macosx/main.mm index 264924edcf1..6b8aef38143 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -168,7 +168,7 @@ int main(int argc, char* argv[]) { map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom); map.setBearing(settings.bearing); map.setPitch(settings.pitch); - map.setDebug(settings.debug); + map.setDebug(mbgl::MapDebugOptions(settings.debug)); view.setChangeStyleCallback([&map, &view] () { static uint8_t currentStyleIndex; @@ -205,7 +205,7 @@ int main(int argc, char* argv[]) { settings.zoom = map.getZoom(); settings.bearing = map.getBearing(); settings.pitch = map.getPitch(); - settings.debug = map.getDebug(); + settings.debug = uint32_t(map.getDebug()); settings.save(); return 0; diff --git a/package.json b/package.json index b15d17b1265..ab033a1978d 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,8 @@ ], "devDependencies": { "aws-sdk": "^2.2.19", - "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#744b63be01e36c75c6e629aec16a53048c0b7dbc", + "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#2e4c2b0e4903410d043ddeb2f5c88adc1dc90b7b", "node-gyp": "^3.2.0", - "read-package-json": "^2.0.2", "request": "^2.67.0", "tape": "^4.2.2" }, diff --git a/platform/android/asset_root.cpp b/platform/android/asset_root.cpp index 4069211f61c..398e18aa113 100644 --- a/platform/android/asset_root.cpp +++ b/platform/android/asset_root.cpp @@ -1,7 +1,6 @@ #include <mbgl/platform/platform.hpp> #include <mbgl/android/jni.hpp> -#include <uv.h> #include <libgen.h> namespace mbgl { diff --git a/platform/android/http_request_android.cpp b/platform/android/http_request_android.cpp index 1c00c5ee5a1..1ffdbff3f7d 100644 --- a/platform/android/http_request_android.cpp +++ b/platform/android/http_request_android.cpp @@ -5,6 +5,7 @@ #include <mbgl/platform/log.hpp> #include <mbgl/android/jni.hpp> +#include <mbgl/util/async_task.hpp> #include <mbgl/util/time.hpp> #include <mbgl/util/util.hpp> #include <mbgl/util/string.hpp> @@ -26,7 +27,6 @@ class HTTPAndroidContext : public HTTPContextBase { HTTPRequestBase* createRequest(const Resource&, RequestBase::Callback, - uv_loop_t*, std::shared_ptr<const Response>) final; JavaVM *vm = nullptr; @@ -38,7 +38,6 @@ class HTTPAndroidRequest : public HTTPRequestBase { HTTPAndroidRequest(HTTPAndroidContext*, const Resource&, Callback, - uv_loop_t*, std::shared_ptr<const Response>); ~HTTPAndroidRequest(); @@ -59,7 +58,7 @@ class HTTPAndroidRequest : public HTTPRequestBase { jobject obj = nullptr; - uv::async async; + util::AsyncTask async; static const int connectionError = 0; static const int temporaryError = 1; @@ -113,24 +112,23 @@ HTTPAndroidContext::~HTTPAndroidContext() { HTTPRequestBase* HTTPAndroidContext::createRequest(const Resource& resource, RequestBase::Callback callback, - uv_loop_t* loop_, std::shared_ptr<const Response> response) { - return new HTTPAndroidRequest(this, resource, callback, loop_, response); + return new HTTPAndroidRequest(this, resource, callback, response); } -HTTPAndroidRequest::HTTPAndroidRequest(HTTPAndroidContext* context_, const Resource& resource_, Callback callback_, uv_loop_t* loop, std::shared_ptr<const Response> response_) +HTTPAndroidRequest::HTTPAndroidRequest(HTTPAndroidContext* context_, const Resource& resource_, Callback callback_, std::shared_ptr<const Response> response_) : HTTPRequestBase(resource_, callback_), context(context_), existingResponse(response_), - async(loop, [this] { finish(); }) { + async([this] { finish(); }) { std::string etagStr; std::string modifiedStr; if (existingResponse) { if (!existingResponse->etag.empty()) { etagStr = existingResponse->etag; - } else if (existingResponse->modified) { - modifiedStr = util::rfc1123(existingResponse->modified); + } else if (existingResponse->modified != Seconds::zero()) { + modifiedStr = util::rfc1123(existingResponse->modified.count()); } } @@ -195,11 +193,11 @@ void HTTPAndroidRequest::onResponse(int code, std::string message, std::string e response = std::make_unique<Response>(); using Error = Response::Error; - response->modified = parse_date(modified.c_str()); + response->modified = Seconds(parse_date(modified.c_str())); response->etag = etag; response->expires = parseCacheControl(cacheControl.c_str()); if (!expires.empty()) { - response->expires = parse_date(expires.c_str()); + response->expires = Seconds(parse_date(expires.c_str())); } response->data = std::make_shared<std::string>(body); @@ -250,7 +248,7 @@ void HTTPAndroidRequest::onFailure(int type, std::string message) { async.send(); } -std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext(uv_loop_t* loop) { +std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() { return std::make_unique<HTTPAndroidContext>(); } diff --git a/platform/android/jni.cpp b/platform/android/jni.cpp index 9b80013da2c..acefc87f31b 100644 --- a/platform/android/jni.cpp +++ b/platform/android/jni.cpp @@ -375,6 +375,7 @@ std::pair<mbgl::AnnotationSegment, mbgl::ShapeAnnotation::Properties> annotation namespace { using namespace mbgl::android; +using DebugOptions = mbgl::MapDebugOptions; jlong JNICALL nativeCreate(JNIEnv *env, jobject obj, jstring cachePath_, jstring dataPath_, jstring apkPath_, jfloat pixelRatio, jint availableProcessors, jlong totalMemory) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreate"); @@ -663,6 +664,20 @@ void JNICALL nativeResetPosition(JNIEnv *env, jobject obj, jlong nativeMapViewPt nativeMapView->getMap().resetPosition(); } +jdouble JNICALL nativeGetPitch(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetPitch"); + assert(nativeMapViewPtr != 0); + NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); + return nativeMapView->getMap().getPitch(); +} + +void JNICALL nativeSetPitch(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble pitch, jlong duration) { + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetPitch"); + assert(nativeMapViewPtr != 0); + NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); + nativeMapView->getMap().setPitch(pitch, std::chrono::milliseconds(duration)); +} + void JNICALL nativeScaleBy(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble ds, jdouble cx, jdouble cy, jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeScaleBy"); @@ -1194,9 +1209,9 @@ jlongArray JNICALL nativeGetAnnotationsInBounds(JNIEnv *env, jobject obj, jlong return std_vector_uint_to_jobject(env, annotations); } -void JNICALL nativeSetSprite(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, +void JNICALL nativeAddAnnotationIcon(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring symbol, jint width, jint height, jfloat scale, jbyteArray jpixels) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetSprite"); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddAnnotationIcon"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); @@ -1213,7 +1228,7 @@ void JNICALL nativeSetSprite(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, float(scale), std::move(pixels)); - nativeMapView->getMap().setSprite(symbolName, spriteImage); + nativeMapView->getMap().addAnnotationIcon(symbolName, spriteImage); } void JNICALL nativeSetVisibleCoordinateBounds(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, @@ -1293,7 +1308,10 @@ void JNICALL nativeSetDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jb mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDebug"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().setDebug(debug); + + DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision + : DebugOptions::NoDebug; + nativeMapView->getMap().setDebug(debugOptions); nativeMapView->enableFps(debug); } @@ -1301,36 +1319,15 @@ void JNICALL nativeToggleDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) mbgl::Log::Debug(mbgl::Event::JNI, "nativeToggleDebug"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().toggleDebug(); - nativeMapView->enableFps(nativeMapView->getMap().getDebug()); + nativeMapView->getMap().cycleDebugOptions(); + nativeMapView->enableFps(nativeMapView->getMap().getDebug() != DebugOptions::NoDebug); } jboolean JNICALL nativeGetDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetDebug"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - return nativeMapView->getMap().getDebug(); -} - -void JNICALL nativeSetCollisionDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jboolean debug) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetCollisionDebug"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().setCollisionDebug(debug); -} - -void JNICALL nativeToggleCollisionDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeToggleCollisionDebug"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().toggleCollisionDebug(); -} - -jboolean JNICALL nativeGetCollisionDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetCollisionDebug"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - return nativeMapView->getMap().getCollisionDebug(); + return nativeMapView->getMap().getDebug() != DebugOptions::NoDebug; } jboolean JNICALL nativeIsFullyLoaded(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { @@ -1471,7 +1468,7 @@ jdouble JNICALL nativeGetTopOffsetPixelsForAnnotationSymbol(JNIEnv *env, jobject mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetTopOffsetPixelsForAnnotationSymbol"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - return nativeMapView->getMap().getTopOffsetPixelsForAnnotationSymbol(std_string_from_jstring(env, symbolName)); + return nativeMapView->getMap().getTopOffsetPixelsForAnnotationIcon(std_string_from_jstring(env, symbolName)); } @@ -1905,6 +1902,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { {"nativeGetLatLng", "(J)Lcom/mapbox/mapboxsdk/geometry/LatLng;", reinterpret_cast<void *>(&nativeGetLatLng)}, {"nativeResetPosition", "(J)V", reinterpret_cast<void *>(&nativeResetPosition)}, + {"nativeGetPitch", "(J)D", reinterpret_cast<void *>(&nativeGetPitch)}, + {"nativeSetPitch", "(JDJ)V", reinterpret_cast<void *>(&nativeSetPitch)}, {"nativeScaleBy", "(JDDDJ)V", reinterpret_cast<void *>(&nativeScaleBy)}, {"nativeSetScale", "(JDDDJ)V", reinterpret_cast<void *>(&nativeSetScale)}, {"nativeGetScale", "(J)D", reinterpret_cast<void *>(&nativeGetScale)}, @@ -1944,16 +1943,13 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { {"nativeRemoveAnnotations", "(J[J)V", reinterpret_cast<void *>(&nativeRemoveAnnotations)}, {"nativeGetAnnotationsInBounds", "(JLcom/mapbox/mapboxsdk/geometry/BoundingBox;)[J", reinterpret_cast<void *>(&nativeGetAnnotationsInBounds)}, - {"nativeSetSprite", "(JLjava/lang/String;IIF[B)V", reinterpret_cast<void *>(&nativeSetSprite)}, + {"nativeAddAnnotationIcon", "(JLjava/lang/String;IIF[B)V", reinterpret_cast<void *>(&nativeAddAnnotationIcon)}, {"nativeSetVisibleCoordinateBounds", "(J[Lcom/mapbox/mapboxsdk/geometry/LatLng;Landroid/graphics/RectF;DJ)V", reinterpret_cast<void *>(&nativeSetVisibleCoordinateBounds)}, {"nativeOnLowMemory", "(J)V", reinterpret_cast<void *>(&nativeOnLowMemory)}, {"nativeSetDebug", "(JZ)V", reinterpret_cast<void *>(&nativeSetDebug)}, {"nativeToggleDebug", "(J)V", reinterpret_cast<void *>(&nativeToggleDebug)}, {"nativeGetDebug", "(J)Z", reinterpret_cast<void *>(&nativeGetDebug)}, - {"nativeSetCollisionDebug", "(JZ)V", reinterpret_cast<void *>(&nativeSetCollisionDebug)}, - {"nativeToggleCollisionDebug", "(J)V", reinterpret_cast<void *>(&nativeToggleCollisionDebug)}, - {"nativeGetCollisionDebug", "(J)Z", reinterpret_cast<void *>(&nativeGetCollisionDebug)}, {"nativeIsFullyLoaded", "(J)Z", reinterpret_cast<void *>(&nativeIsFullyLoaded)}, {"nativeSetReachability", "(JZ)V", reinterpret_cast<void *>(&nativeSetReachability)}, {"nativeGetMetersPerPixelAtLatitude", "(JDD)D", reinterpret_cast<void *>(&nativeGetMetersPerPixelAtLatitude)}, diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm index 5c78bfc90fb..372fa8d619d 100644 --- a/platform/darwin/http_request_nsurl.mm +++ b/platform/darwin/http_request_nsurl.mm @@ -3,6 +3,7 @@ #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> +#include <mbgl/util/async_task.hpp> #include <mbgl/util/time.hpp> #include <mbgl/util/parsedate.h> @@ -20,7 +21,6 @@ HTTPNSURLRequest(HTTPNSURLContext*, const Resource&, Callback, - uv_loop_t*, std::shared_ptr<const Response>); ~HTTPNSURLRequest(); @@ -35,7 +35,7 @@ NSURLSessionDataTask *task = nullptr; std::unique_ptr<Response> response; const std::shared_ptr<const Response> existingResponse; - uv::async async; + util::AsyncTask async; }; // ------------------------------------------------------------------------------------------------- @@ -47,7 +47,6 @@ HTTPRequestBase* createRequest(const Resource&, RequestBase::Callback, - uv_loop_t*, std::shared_ptr<const Response>) final; NSURLSession *session = nil; @@ -84,9 +83,8 @@ HTTPRequestBase* HTTPNSURLContext::createRequest(const Resource& resource, RequestBase::Callback callback, - uv_loop_t* loop, std::shared_ptr<const Response> response) { - return new HTTPNSURLRequest(this, resource, callback, loop, response); + return new HTTPNSURLRequest(this, resource, callback, response); } // ------------------------------------------------------------------------------------------------- @@ -94,12 +92,11 @@ HTTPNSURLRequest::HTTPNSURLRequest(HTTPNSURLContext* context_, const Resource& resource_, Callback callback_, - uv_loop_t* loop, std::shared_ptr<const Response> existingResponse_) : HTTPRequestBase(resource_, callback_), context(context_), existingResponse(existingResponse_), - async(loop, [this] { handleResponse(); }) { + async([this] { handleResponse(); }) { @autoreleasepool { NSURL* url = [NSURL URLWithString:@(resource.url.c_str())]; if (context->accountType == 0 && @@ -114,8 +111,8 @@ if (!existingResponse->etag.empty()) { [req addValue:@(existingResponse->etag.c_str()) forHTTPHeaderField:@"If-None-Match"]; - } else if (existingResponse->modified) { - const std::string time = util::rfc1123(existingResponse->modified); + } else if (existingResponse->modified != Seconds::zero()) { + const std::string time = util::rfc1123(existingResponse->modified.count()); [req addValue:@(time.c_str()) forHTTPHeaderField:@"If-Modified-Since"]; } } @@ -214,12 +211,12 @@ NSString *expires = [headers objectForKey:@"Expires"]; if (expires) { - response->expires = parse_date([expires UTF8String]); + response->expires = Seconds(parse_date([expires UTF8String])); } NSString *last_modified = [headers objectForKey:@"Last-Modified"]; if (last_modified) { - response->modified = parse_date([last_modified UTF8String]); + response->modified = Seconds(parse_date([last_modified UTF8String])); } NSString *etag = [headers objectForKey:@"ETag"]; @@ -264,7 +261,7 @@ async.send(); } -std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext(uv_loop_t*) { +std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() { return std::make_unique<HTTPNSURLContext>(); } diff --git a/platform/default/asset_request_fs.cpp b/platform/default/asset_request_fs.cpp index c169abf2b8c..3ff0e62cfdf 100644 --- a/platform/default/asset_request_fs.cpp +++ b/platform/default/asset_request_fs.cpp @@ -1,60 +1,123 @@ #include <mbgl/storage/asset_context_base.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> -#include <mbgl/util/util.hpp> -#include <mbgl/util/url.hpp> -#include <mbgl/util/uv.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/thread.hpp> +#include <mbgl/util/url.hpp> +#include <mbgl/util/util.hpp> + +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace { + +struct FDHolder { + FDHolder(int fd_) : fd(fd_) {} + + ~FDHolder() { + if (fd >= 0) ::close(fd); + } + + int fd; +}; + +class FileIOWorker { +public: + FileIOWorker() = default; + + void open(const std::string& path, int flags, mode_t mode, + std::function<void(std::unique_ptr<FDHolder>)> cb) { + // FDHolder is needed because if the request gets canceled + // we would otherwise end up with a fd leaking. + int ret = ::open(path.c_str(), flags, mode); + cb(std::make_unique<FDHolder>(ret < 0 ? -errno : ret)); + } + + void fstat(int fd, std::function<void (int, std::unique_ptr<struct stat>)> cb) { + std::unique_ptr<struct stat> buf = std::make_unique<struct stat>(); + int ret = ::fstat(fd, buf.get()); + cb(ret < 0 ? -errno : ret, std::move(buf)); + } + + void read(int fd, size_t size, + std::function<void (ssize_t, std::shared_ptr<std::string>)> cb) { + std::shared_ptr<std::string> buf = std::make_shared<std::string>(); + buf->resize(size); + + ssize_t ret = ::read(fd, &buf->front(), size); + cb(ret < 0 ? -errno : ret, std::move(buf)); + } -#include <uv.h> + void close(int fd, std::function<void (void)> cb) { + ::close(fd); + cb(); + } +}; -#include <cassert> -#include <limits> +} // namespace namespace mbgl { +using namespace std::placeholders; + class AssetRequest : public RequestBase { MBGL_STORE_THREAD(tid) public: - AssetRequest(const Resource&, Callback, uv_loop_t*, const std::string& assetRoot); + AssetRequest(const Resource&, Callback, const std::string& assetRoot, + util::Thread<FileIOWorker> *worker); ~AssetRequest(); + // RequestBase implementation. void cancel() final; - static void fileOpened(uv_fs_t *req); - static void fileStated(uv_fs_t *req); - static void fileRead(uv_fs_t *req); - static void fileClosed(uv_fs_t *req); - static void notifyError(uv_fs_t *req); - static void cleanup(uv_fs_t *req); +private: + void fileOpened(std::unique_ptr<FDHolder>); + void fileStated(int result, std::unique_ptr<struct stat>); + void fileRead(int result, std::shared_ptr<std::string>); + void fileClosed(); + + void notifyError(int result); + + void close(); + void cleanup(); std::string assetRoot; - bool canceled = false; - uv_fs_t req; - uv_file fd = -1; - uv_buf_t buffer; std::unique_ptr<Response> response; + + int fd = -1; + + util::Thread<FileIOWorker> *worker; + std::unique_ptr<WorkRequest> request; }; class AssetFSContext : public AssetContextBase { +public: + AssetFSContext() + : worker({"FileIOWorker", util::ThreadType::Worker, util::ThreadPriority::Regular}) {} + +private: RequestBase* createRequest(const Resource& resource, RequestBase::Callback callback, - uv_loop_t* loop, const std::string& assetRoot) final { - return new AssetRequest(resource, callback, loop, assetRoot); + return new AssetRequest(resource, callback, assetRoot, &worker); } + + util::Thread<FileIOWorker> worker; }; AssetRequest::~AssetRequest() { MBGL_VERIFY_THREAD(tid); } -AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, uv_loop_t* loop, const std::string& assetRoot_) +AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, + const std::string& assetRoot_, util::Thread<FileIOWorker> *worker_) : RequestBase(resource_, callback_), - assetRoot(assetRoot_) { - req.data = this; - + assetRoot(assetRoot_), + worker(worker_) { const auto &url = resource.url; std::string path; if (url.size() <= 8 || url[8] == '/') { @@ -65,160 +128,109 @@ AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, uv_loo path = assetRoot + "/" + mbgl::util::percentDecode(url.substr(8)); } - uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, fileOpened); + request = worker->invokeWithCallback(&FileIOWorker::open, + std::bind(&AssetRequest::fileOpened, this, _1), path, O_RDONLY, S_IRUSR); } -void AssetRequest::fileOpened(uv_fs_t *req) { - assert(req->data); - auto self = reinterpret_cast<AssetRequest *>(req->data); - MBGL_VERIFY_THREAD(self->tid); +void AssetRequest::cancel() { + MBGL_VERIFY_THREAD(tid); - if (req->result < 0) { - // Opening failed or was canceled. There isn't much left we can do. - notifyError(req); - cleanup(req); - } else { - const uv_file fd = uv_file(req->result); - - // We're going to reuse this handle, so we need to cleanup first. - uv_fs_req_cleanup(req); - - if (self->canceled) { - // The request was canceled. - uv_fs_close(req->loop, req, fd, fileClosed); - } else { - self->fd = fd; - uv_fs_fstat(req->loop, req, fd, fileStated); - } + request.reset(); + close(); +} + +void AssetRequest::fileOpened(std::unique_ptr<FDHolder> holder) { + MBGL_VERIFY_THREAD(tid); + + std::swap(fd, holder->fd); + + if (fd < 0) { + // Opening failed. There isn't much left we can do. + notifyError(fd); + cleanup(); + + return; } + + request = worker->invokeWithCallback(&FileIOWorker::fstat, + std::bind(&AssetRequest::fileStated, this, _1, _2), fd); } -void AssetRequest::fileStated(uv_fs_t *req) { - assert(req->data); - auto self = reinterpret_cast<AssetRequest *>(req->data); - MBGL_VERIFY_THREAD(self->tid); +void AssetRequest::fileStated(int result, std::unique_ptr<struct stat> stat) { + MBGL_VERIFY_THREAD(tid); - if (req->result != 0 || self->canceled) { - // Stating failed or was canceled. We already have an open file handle + if (result != 0) { + // Stating failed. We already have an open file handle // though, which we'll have to close. - notifyError(req); - - uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, self->fd, fileClosed); + notifyError(result); + close(); } else { -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - auto stat = static_cast<const uv_statbuf_t *>(req->ptr); -#else - auto stat = static_cast<const uv_stat_t *>(req->ptr); -#endif - if (stat->st_size > std::numeric_limits<int>::max()) { - // File is too large for us to open this way because uv_buf's only support unsigned - // ints as maximum size. - auto response = std::make_unique<Response>(); -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - auto message = uv_strerror(uv_err_t {UV_EFBIG, 0}); -#else - auto message = uv_strerror(UV_EFBIG); -#endif - response->error = - std::make_unique<Response::Error>(Response::Error::Reason::Other, message); - self->notify(std::move(response)); - - uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, self->fd, fileClosed); - } else { - self->response = std::make_unique<Response>(); -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -#ifdef __APPLE__ - self->response->modified = stat->st_mtimespec.tv_sec; -#else - self->response->modified = stat->st_mtime; -#endif -#else - self->response->modified = stat->st_mtim.tv_sec; -#endif - self->response->etag = util::toString(stat->st_ino); - const auto size = (unsigned int)(stat->st_size); - auto data = std::make_shared<std::string>(); - self->response->data = data; - data->resize(size); - self->buffer = uv_buf_init(const_cast<char *>(data->data()), size); - uv_fs_req_cleanup(req); -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_fs_read(req->loop, req, self->fd, self->buffer.base, self->buffer.len, -1, fileRead); -#else - uv_fs_read(req->loop, req, self->fd, &self->buffer, 1, 0, fileRead); -#endif - } + response = std::make_unique<Response>(); + response->modified = Seconds(stat->st_mtime); + response->etag = util::toString(stat->st_ino); + + request = worker->invokeWithCallback(&FileIOWorker::read, + std::bind(&AssetRequest::fileRead, this, _1, _2), fd, stat->st_size); } } -void AssetRequest::fileRead(uv_fs_t *req) { - assert(req->data); - auto self = reinterpret_cast<AssetRequest *>(req->data); - MBGL_VERIFY_THREAD(self->tid); +void AssetRequest::fileRead(int result, std::shared_ptr<std::string> data) { + MBGL_VERIFY_THREAD(tid); - if (req->result < 0 || self->canceled) { - // Stating failed or was canceled. We already have an open file handle + if (result < 0) { + // Reading failed. We already have an open file handle // though, which we'll have to close. - notifyError(req); + notifyError(result); } else { - // File was successfully read. - self->notify(std::move(self->response)); + response->data = data; + notify(std::move(response)); } - uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, self->fd, fileClosed); + close(); } -void AssetRequest::fileClosed(uv_fs_t *req) { - assert(req->data); - auto self = reinterpret_cast<AssetRequest *>(req->data); - MBGL_VERIFY_THREAD(self->tid); - (void(self)); // Silence unused variable error in Release mode - - if (req->result < 0) { - // Closing the file failed. But there isn't anything we can do. - } +void AssetRequest::fileClosed() { + MBGL_VERIFY_THREAD(tid); - cleanup(req); + cleanup(); } -void AssetRequest::notifyError(uv_fs_t *req) { - assert(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 = std::make_unique<Response>(); - Response::Error::Reason reason = Response::Error::Reason::Other; - if (req->result == UV_ENOENT || req->result == UV_EISDIR) { - reason = Response::Error::Reason::NotFound; - } - response->error = std::make_unique<Response::Error>(reason, - uv::getFileRequestError(req)); - self->notify(std::move(response)); +void AssetRequest::close() { + MBGL_VERIFY_THREAD(tid); + + if (fd >= 0) { + request = worker->invokeWithCallback(&FileIOWorker::close, + std::bind(&AssetRequest::fileClosed, this), fd); + fd = -1; + } else { + cleanup(); } } -void AssetRequest::cleanup(uv_fs_t *req) { - assert(req->data); - auto self = reinterpret_cast<AssetRequest *>(req->data); - MBGL_VERIFY_THREAD(self->tid); - uv_fs_req_cleanup(req); - delete self; +void AssetRequest::cleanup() { + MBGL_VERIFY_THREAD(tid); + + delete this; } -void AssetRequest::cancel() { - 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); +void AssetRequest::notifyError(int result) { + MBGL_VERIFY_THREAD(tid); + + result = std::abs(result); + Response::Error::Reason reason = Response::Error::Reason::Other; + + if (result == ENOENT || result == EISDIR) { + reason = Response::Error::Reason::NotFound; + } + + response = std::make_unique<Response>(); + response->error = std::make_unique<Response::Error>(reason, ::strerror(result)); + + notify(std::move(response)); } -std::unique_ptr<AssetContextBase> AssetContextBase::createContext(uv_loop_t*) { +std::unique_ptr<AssetContextBase> AssetContextBase::createContext() { return std::make_unique<AssetFSContext>(); } -} +} // namespace mbgl diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp index 2c0c54b6a0a..b29166f673e 100644 --- a/platform/default/asset_request_zip.cpp +++ b/platform/default/asset_request_zip.cpp @@ -1,115 +1,201 @@ #include <mbgl/storage/asset_context_base.hpp> -#include <mbgl/android/jni.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> -#include <mbgl/platform/log.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/util.hpp> -#include <mbgl/util/uv.hpp> +#include <mbgl/util/thread.hpp> -#include <uv.h> -#include "uv_zip.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include <zip.h> +#pragma GCC diagnostic pop -#include <map> #include <cassert> #include <forward_list> +#include <map> -namespace mbgl { +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> -class AssetZipContext : public AssetContextBase { -public: - explicit AssetZipContext(uv_loop_t *loop); - ~AssetZipContext(); +namespace { - RequestBase* createRequest(const Resource& resource, - RequestBase::Callback callback, - uv_loop_t* loop, - const std::string& assetRoot) final; +struct ZipHolder { + ZipHolder(struct zip* archive_) : archive(archive_) {} - uv_zip_t *getHandle(const std::string &path); - void returnHandle(const std::string &path, uv_zip_t *zip); + ~ZipHolder() { + if (archive) ::zip_discard(archive); + } - // 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; + struct zip* archive; }; -AssetZipContext::AssetZipContext(uv_loop_t *loop_) : loop(loop_) { -} +struct ZipFileHolder { + ZipFileHolder(struct zip_file* file_) : file(file_) {} -uv_zip_t *AssetZipContext::getHandle(const std::string &path) { - auto &list = handles[path]; - if (!list.empty()) { - auto zip = list.front(); - list.pop_front(); - return zip; - } else { - return nullptr; + ~ZipFileHolder() { + if (file) ::zip_fclose(file); } -} -void AssetZipContext::returnHandle(const std::string &path, uv_zip_t *zip) { - handles[path].push_front(zip); -} + struct zip_file* file; +}; -AssetZipContext::~AssetZipContext() { - // Close all zip handles - for (auto &list : handles) { - for (auto zip : list.second) { - uv_zip_discard(loop, zip, [](uv_zip_t *zip_) { - uv_zip_cleanup(zip_); - delete zip_; - }); +class ZipIOWorker { +public: + ZipIOWorker() = default; + + void zip_fdopen(const std::string& path, + std::function<void(std::unique_ptr<ZipHolder>)> cb) { + int fd = ::open(path.c_str(), O_RDONLY, S_IRUSR); + if (fd < 0) { + cb(std::make_unique<ZipHolder>(nullptr)); + } else { + int errorp; + struct zip* archive = ::zip_fdopen(fd, 0, &errorp); + cb(std::make_unique<ZipHolder>(archive)); } } - handles.clear(); + + void zip_stat(struct zip* archive, const std::string& path, + std::function<void (int, std::unique_ptr<struct zip_stat>)> cb) { + std::unique_ptr<struct zip_stat> buf = std::make_unique<struct zip_stat>(); + ::zip_stat_init(buf.get()); + + int ret = ::zip_stat(archive, path.c_str(), 0, buf.get()); + cb(ret, std::move(buf)); + } + + void zip_fopen(struct zip* archive, const std::string& path, + std::function<void (std::unique_ptr<ZipFileHolder>)> cb) { + struct zip_file* file = ::zip_fopen(archive, path.c_str(), 0); + cb(std::make_unique<ZipFileHolder>(file)); + } + + void zip_fread(struct zip_file* file, size_t size, + std::function<void (int, std::shared_ptr<std::string>)> cb) { + std::shared_ptr<std::string> buf = std::make_shared<std::string>(); + buf->resize(size); + + int ret = ::zip_fread(file, &buf->front(), size); + cb(ret, std::move(buf)); + } + + void zip_fclose(struct zip_file* file, std::function<void (int)> cb) { + cb(::zip_fclose(file)); + } +}; + } +namespace mbgl { + +using namespace std::placeholders; + +class AssetZipContext; + class AssetRequest : public RequestBase { MBGL_STORE_THREAD(tid) public: - AssetRequest(AssetZipContext&, const Resource&, Callback, const std::string& assetRoot); + AssetRequest(const Resource&, Callback, const std::string& assetRoot, + AssetZipContext* context, util::Thread<ZipIOWorker>* worker); ~AssetRequest(); + // RequestBase implementation. void cancel() final; private: - AssetZipContext &context; - bool cancelled = false; + void openArchive(); + void archiveOpened(std::unique_ptr<ZipHolder>); + void fileStated(int result, std::unique_ptr<struct zip_stat>); + void fileOpened(std::unique_ptr<ZipFileHolder>); + void fileRead(int result, std::shared_ptr<std::string>); + void fileClosed(); + + void close(); + void cleanup(); + + void notifyError(Response::Error::Reason, const char *message); + const std::string root; const std::string path; std::unique_ptr<Response> response; - uv_buf_t buffer; -private: - void openZipArchive(); - void archiveOpened(uv_zip_t *zip); - void fileStated(uv_zip_t *zip); - void fileOpened(uv_zip_t *zip); - void fileRead(uv_zip_t *zip); - void fileClosed(uv_zip_t *zip); - void cleanup(uv_zip_t *zip); - - void notifyError(const char *message, Response::Error::Reason); + struct zip* archive = nullptr; + struct zip_file* file = nullptr; + size_t size = 0; + + AssetZipContext *context; + + util::Thread<ZipIOWorker> *worker; + std::unique_ptr<WorkRequest> request; +}; + +class AssetZipContext : public AssetContextBase { +public: + AssetZipContext(); + ~AssetZipContext(); + + RequestBase* createRequest(const Resource& resource, + RequestBase::Callback callback, + const std::string& assetRoot) final { + return new AssetRequest(resource, callback, assetRoot, this, &worker); + } + + struct zip *getHandle(const std::string &path); + void returnHandle(const std::string &path, struct zip *zip); + + // A list of resuable zip handles to avoid creating + // and destroying them all the time. + std::map<std::string, std::forward_list<struct zip*>> handles; + + util::Thread<ZipIOWorker> worker; + std::unique_ptr<WorkRequest> request; }; -RequestBase* AssetZipContext::createRequest(const Resource& resource, - RequestBase::Callback callback, - uv_loop_t*, - const std::string& assetRoot) { - return new AssetRequest(*this, resource, callback, assetRoot); +AssetZipContext::AssetZipContext() + : worker({"ZipIOWorker", util::ThreadType::Worker, util::ThreadPriority::Regular}) {} + +AssetZipContext::~AssetZipContext() { + // Close all zip handles + for (auto &list : handles) { + for (auto archive : list.second) { + zip_discard(archive); + } + } + + handles.clear(); } -AssetRequest::AssetRequest(AssetZipContext& context_, const Resource& resource_, Callback callback_, const std::string& assetRoot_) +struct zip* AssetZipContext::getHandle(const std::string &path) { + auto &list = handles[path]; + if (!list.empty()) { + auto archive = list.front(); + list.pop_front(); + return archive; + } else { + return nullptr; + } +} + +void AssetZipContext::returnHandle(const std::string &path, struct zip* archive) { + handles[path].push_front(archive); +} + +AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, + const std::string& assetRoot, AssetZipContext* context_, util::Thread<ZipIOWorker>* worker_) : RequestBase(resource_, callback_), + root(assetRoot), + path(std::string("assets/") + resource.url.substr(8)), context(context_), - root(assetRoot_), - path(std::string { "assets/" } + resource.url.substr(8)) { - auto zip = context.getHandle(root); - if (zip) { - archiveOpened(zip); + worker(worker_) { + archive = context->getHandle(root); + if (archive) { + request = worker->invokeWithCallback(&ZipIOWorker::zip_stat, + std::bind(&AssetRequest::fileStated, this, _1, _2), archive, path); } else { - openZipArchive(); + request = worker->invokeWithCallback(&ZipIOWorker::zip_fdopen, + std::bind(&AssetRequest::archiveOpened, this, _1), root); } } @@ -117,150 +203,118 @@ AssetRequest::~AssetRequest() { MBGL_VERIFY_THREAD(tid); } -void AssetRequest::openZipArchive() { - uv_fs_t *req = new uv_fs_t(); - req->data = this; - - // 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<AssetRequest *>(fsReq->data); - impl->notifyError(uv::getFileRequestError(fsReq), Response::Error::Reason::Connection); - delete impl; - } else { - uv_zip_t *zip = new uv_zip_t(); - 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<AssetRequest *>(openZip->data); - if (openZip->result < 0) { - impl->notifyError(openZip->message, Response::Error::Reason::Other); - delete openZip; - delete impl; - } else { - impl->archiveOpened(openZip); - } - }); - } +void AssetRequest::cancel() { + MBGL_VERIFY_THREAD(tid); - uv_fs_req_cleanup(fsReq); - delete fsReq; - fsReq = nullptr; - }); + request.reset(); + close(); } -#define INVOKE_MEMBER(name) \ - [](uv_zip_t *zip_) { \ - assert(zip_->data); \ - reinterpret_cast<AssetRequest *>(zip_->data)->name(zip_); \ - } - -void AssetRequest::archiveOpened(uv_zip_t *zip) { +void AssetRequest::archiveOpened(std::unique_ptr<ZipHolder> holder) { MBGL_VERIFY_THREAD(tid); - zip->data = this; - uv_zip_stat(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileStated)); + std::swap(archive, holder->archive); + + if (!archive) { + notifyError(Response::Error::Reason::Other, "Could not open zip archive"); + cleanup(); + } else { + request = worker->invokeWithCallback(&ZipIOWorker::zip_stat, + std::bind(&AssetRequest::fileStated, this, _1, _2), archive, path); + } } -void AssetRequest::fileStated(uv_zip_t *zip) { +void AssetRequest::fileStated(int result, std::unique_ptr<struct zip_stat> stat) { MBGL_VERIFY_THREAD(tid); - if (cancelled || zip->result < 0) { - // Stat failed, probably because the file doesn't exist. - if (zip->result < 0) { - notifyError(zip->message, Response::Error::Reason::NotFound); - } - cleanup(zip); - } else if (!(zip->stat->valid & ZIP_STAT_SIZE)) { - // We couldn't obtain the size of the file. - notifyError("Could not determine file size in zip file", Response::Error::Reason::Other); - cleanup(zip); + if (result < 0 || !(stat->valid & ZIP_STAT_SIZE)) { + notifyError(Response::Error::Reason::NotFound, "Could not stat file in zip archive"); + cleanup(); } else { response = std::make_unique<Response>(); - // Allocate the space for reading the data. - auto data = std::make_shared<std::string>(); - data->resize(zip->stat->size); - buffer = uv_buf_init(const_cast<char *>(data->data()), zip->stat->size); - response->data = data; - - // Get the modification time in case we have one. - if (zip->stat->valid & ZIP_STAT_MTIME) { - response->modified = zip->stat->mtime; + if (stat->valid & ZIP_STAT_MTIME) { + response->modified = Seconds(stat->mtime); } - if (zip->stat->valid & ZIP_STAT_INDEX) { - response->etag = std::to_string(zip->stat->index); + if (stat->valid & ZIP_STAT_INDEX) { + response->etag = std::to_string(stat->index); } - uv_zip_fopen(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileOpened)); + size = stat->size; + + request = worker->invokeWithCallback(&ZipIOWorker::zip_fopen, + std::bind(&AssetRequest::fileOpened, this, _1), archive, path); } } -void AssetRequest::fileOpened(uv_zip_t *zip) { +void AssetRequest::fileOpened(std::unique_ptr<ZipFileHolder> holder) { MBGL_VERIFY_THREAD(tid); - if (zip->result < 0) { - // Opening failed. - notifyError(zip->message, Response::Error::Reason::Other); - cleanup(zip); - } else if (cancelled) { - // The request was canceled. Close the file again. - uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed)); + std::swap(file, holder->file); + + if (!file) { + notifyError(Response::Error::Reason::NotFound, "Could not open file in zip archive"), + cleanup(); } else { - uv_zip_fread(context.loop, zip, zip->file, &buffer, INVOKE_MEMBER(fileRead)); + request = worker->invokeWithCallback(&ZipIOWorker::zip_fread, + std::bind(&AssetRequest::fileRead, this, _1, _2), file, size); } } -void AssetRequest::fileRead(uv_zip_t *zip) { +void AssetRequest::fileRead(int result, std::shared_ptr<std::string> data) { MBGL_VERIFY_THREAD(tid); - if (zip->result < 0) { - // Reading failed. We still have an open file handle though. - notifyError(zip->message, Response::Error::Reason::Other); - } else if (!cancelled) { + if (result < 0) { + notifyError(Response::Error::Reason::Other, "Could not read file in zip archive"); + } else { + response->data = data; notify(std::move(response)); } - uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed)); + close(); } -void AssetRequest::fileClosed(uv_zip_t *zip) { +void AssetRequest::fileClosed() { MBGL_VERIFY_THREAD(tid); - if (zip->result < 0) { - // Closing the file failed. But there isn't anything we can do. - } - - cleanup(zip); + cleanup(); } -void AssetRequest::cleanup(uv_zip_t *zip) { + +void AssetRequest::close() { MBGL_VERIFY_THREAD(tid); - context.returnHandle(root, zip); - delete this; + if (file) { + request = worker->invokeWithCallback(&ZipIOWorker::zip_fclose, + std::bind(&AssetRequest::fileClosed, this), file); + file = nullptr; + } else { + cleanup(); + } } -void AssetRequest::notifyError(const char *message, Response::Error::Reason reason) { +void AssetRequest::cleanup() { MBGL_VERIFY_THREAD(tid); - if (!cancelled) { - response = std::make_unique<Response>(); - response->error = std::make_unique<Response::Error>(reason, message); - notify(std::move(response)); - } else { - // The request was already canceled and deleted. + if (archive) { + context->returnHandle(root, archive); } + + delete this; } -void AssetRequest::cancel() { - cancelled = true; +void AssetRequest::notifyError(Response::Error::Reason reason, const char *message) { + MBGL_VERIFY_THREAD(tid); + + response = std::make_unique<Response>(); + response->error = std::make_unique<Response::Error>(reason, message); + + notify(std::move(response)); } -std::unique_ptr<AssetContextBase> AssetContextBase::createContext(uv_loop_t* loop) { - return std::make_unique<AssetZipContext>(loop); +std::unique_ptr<AssetContextBase> AssetContextBase::createContext() { + return std::make_unique<AssetZipContext>(); } } diff --git a/platform/default/async_task.cpp b/platform/default/async_task.cpp new file mode 100644 index 00000000000..c8fb4dcd138 --- /dev/null +++ b/platform/default/async_task.cpp @@ -0,0 +1,79 @@ +#include <mbgl/util/async_task.hpp> + +#include <mbgl/util/run_loop.hpp> + +#include <atomic> +#include <functional> + +#include <uv.h> + +#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 { + +class AsyncTask::Impl { +public: + Impl(std::function<void()>&& fn) + : async(new uv_async_t), + task(std::move(fn)) { + + uv_loop_t* loop = reinterpret_cast<uv_loop_t*>(RunLoop::getLoopHandle()); + if (uv_async_init(loop, async, asyncCallback) != 0) { + throw std::runtime_error("Failed to initialize async."); + } + + handle()->data = this; + } + + ~Impl() { + uv_close(handle(), [](uv_handle_t* h) { + delete reinterpret_cast<uv_async_t*>(h); + }); + } + + void maySend() { + // uv_async_send will do the call coalescing for us. + if (uv_async_send(async) != 0) { + throw std::runtime_error("Failed to async send."); + } + } + + void unref() { + uv_unref(handle()); + } + +private: + static void asyncCallback(UV_ASYNC_PARAMS(handle)) { + reinterpret_cast<Impl*>(handle->data)->task(); + } + + uv_handle_t* handle() { + return reinterpret_cast<uv_handle_t*>(async); + } + + uv_async_t* async; + + std::function<void()> task; +}; + +AsyncTask::AsyncTask(std::function<void()>&& fn) + : impl(std::make_unique<Impl>(std::move(fn))) { +} + +AsyncTask::~AsyncTask() = default; + +void AsyncTask::send() { + impl->maySend(); +} + +void AsyncTask::unref() { + impl->unref(); +} + +} // namespace util +} // namespace mbgl diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index eae2c3610a1..302f108e2f8 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -89,7 +89,6 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_) printf("- Press `S` to cycle through bundled styles\n"); printf("- Press `X` to reset the transform\n"); printf("- Press `N` to reset north\n"); - printf("- Press `C` to toggle symbol collision debug boxes\n"); printf("- Press `R` to toggle any available `night` style class\n"); printf("\n"); printf("- Press `1` through `6` to add increasing numbers of point annotations for testing\n"); @@ -102,7 +101,7 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_) printf("- `Control` + mouse drag to rotate\n"); printf("- `Shift` + mouse drag to tilt\n"); printf("\n"); - printf("- Press `Tab` to toggle debug information\n"); + printf("- Press `Tab` to cycle through the map debug options\n"); printf("- Press `Esc` to quit\n"); printf("\n"); printf("================================================================================\n"); @@ -116,6 +115,7 @@ GLFWView::~GLFWView() { void GLFWView::initialize(mbgl::Map *map_) { View::initialize(map_); + map->addAnnotationIcon("default_marker", makeSpriteImage(22, 22, 1)); } void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) { @@ -127,10 +127,7 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, glfwSetWindowShouldClose(window, true); break; case GLFW_KEY_TAB: - view->map->toggleDebug(); - break; - case GLFW_KEY_C: - view->map->toggleCollisionDebug(); + view->map->cycleDebugOptions(); break; case GLFW_KEY_X: if (!mods) @@ -225,7 +222,7 @@ void GLFWView::addRandomCustomPointAnnotations(int count) { for (int i = 0; i < count; i++) { static int spriteID = 1; const auto name = std::string{ "marker-" } + mbgl::util::toString(spriteID++); - map->setSprite(name, makeSpriteImage(22, 22, 1)); + map->addAnnotationIcon(name, makeSpriteImage(22, 22, 1)); spriteIDs.push_back(name); points.emplace_back(makeRandomPoint(), name); } @@ -238,7 +235,7 @@ void GLFWView::addRandomPointAnnotations(int count) { std::vector<mbgl::PointAnnotation> points; for (int i = 0; i < count; i++) { - points.emplace_back(makeRandomPoint(), "marker-15"); + points.emplace_back(makeRandomPoint(), "default_marker"); } auto newIDs = map->addPointAnnotations(points); @@ -539,5 +536,5 @@ void showColorDebugImage(std::string name, const char *data, size_t logicalWidth } #endif -} -} +} // namespace platform +} // namespace mbgl diff --git a/platform/default/headless_display.cpp b/platform/default/headless_display.cpp index c4535fc1a66..09310570641 100644 --- a/platform/default/headless_display.cpp +++ b/platform/default/headless_display.cpp @@ -81,4 +81,4 @@ HeadlessDisplay::~HeadlessDisplay() { #endif } -} +} // namespace mbgl diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index d6fd892700d..55a8c577738 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -8,6 +8,7 @@ #include <string> #include <cstring> #include <cassert> +#include <utility> #ifdef MBGL_USE_CGL #include <CoreFoundation/CoreFoundation.h> @@ -26,7 +27,7 @@ HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display_, float pixelRatio_, uint16_t width, uint16_t height) - : display(display_), pixelRatio(pixelRatio_) { + : display(std::move(display_)), pixelRatio(pixelRatio_) { resize(width, height); } @@ -185,7 +186,7 @@ PremultipliedImage HeadlessView::readStillImage() { std::memcpy(rgba + j * stride, tmp.get(), stride); } - return std::move(image); + return image; } void HeadlessView::clearBuffers() { @@ -303,4 +304,4 @@ void HeadlessView::afterRender() { // no-op } -} +} // namespace mbgl diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp index 6d4011dd2b4..065b90501b2 100644 --- a/platform/default/http_request_curl.cpp +++ b/platform/default/http_request_curl.cpp @@ -6,7 +6,9 @@ #include <mbgl/util/time.hpp> #include <mbgl/util/util.hpp> +#include <mbgl/util/run_loop.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/timer.hpp> #include <curl/curl.h> @@ -22,12 +24,6 @@ #include <cstring> #include <cstdio> -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -#define UV_TIMER_PARAMS(timer) uv_timer_t *timer, int -#else -#define UV_TIMER_PARAMS(timer) uv_timer_t *timer -#endif - void handleError(CURLMcode code) { if (code != CURLM_OK) { throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(code)); @@ -48,27 +44,24 @@ class HTTPCURLContext : public HTTPContextBase { MBGL_STORE_THREAD(tid) public: - explicit HTTPCURLContext(uv_loop_t *loop); + HTTPCURLContext(); ~HTTPCURLContext(); HTTPRequestBase* createRequest(const Resource&, RequestBase::Callback, - uv_loop_t*, std::shared_ptr<const Response>) final; 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); - static void onTimeout(UV_TIMER_PARAMS(req)); + static void onTimeout(HTTPCURLContext *context); + void perform(curl_socket_t s, util::RunLoop::Event event); CURL *getHandle(); void returnHandle(CURL *handle); void checkMultiInfo(); - uv_loop_t *loop = nullptr; - // Used as the CURL timer function to periodically check for socket updates. - uv_timer_t *timeout = nullptr; + util::Timer timeout; // CURL multi handle that we use to request multiple URLs at the same time, without having to // block and spawn threads. @@ -89,7 +82,6 @@ class HTTPCURLRequest : public HTTPRequestBase { HTTPCURLRequest(HTTPCURLContext*, const Resource&, Callback, - uv_loop_t*, std::shared_ptr<const Response>); ~HTTPCURLRequest(); @@ -116,55 +108,13 @@ class HTTPCURLRequest : public HTTPRequestBase { char error[CURL_ERROR_SIZE]; }; - - -struct Socket { -private: - uv_poll_t poll; - -public: - HTTPCURLContext *context = nullptr; - const curl_socket_t sockfd = 0; - -public: - Socket(HTTPCURLContext *context_, curl_socket_t sockfd_) : context(context_), sockfd(sockfd_) { - assert(context); - uv_poll_init_socket(context->loop, &poll, sockfd); - poll.data = this; - } - - void start(int events, uv_poll_cb cb) { - uv_poll_start(&poll, events, cb); - } - - void stop() { - assert(poll.data); - uv_poll_stop(&poll); - uv_close((uv_handle_t *)&poll, [](uv_handle_t *handle) { - assert(handle->data); - delete reinterpret_cast<Socket *>(handle->data); - }); - } - -private: - // Make the destructor private to ensure that we can only close the Socket - // with stop(), and disallow manual deletion. - ~Socket() = default; -}; - // ------------------------------------------------------------------------------------------------- -HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_) - : HTTPContextBase(), - loop(loop_) { +HTTPCURLContext::HTTPCURLContext() { if (curl_global_init(CURL_GLOBAL_ALL)) { throw std::runtime_error("Could not init cURL"); } - timeout = new uv_timer_t; - timeout->data = this; - uv_timer_init(loop, timeout); - share = curl_share_init(); multi = curl_multi_init(); @@ -186,15 +136,13 @@ HTTPCURLContext::~HTTPCURLContext() { curl_share_cleanup(share); share = nullptr; - uv_timer_stop(timeout); - uv::close(timeout); + timeout.stop(); } HTTPRequestBase* HTTPCURLContext::createRequest(const Resource& resource, RequestBase::Callback callback, - uv_loop_t* loop_, std::shared_ptr<const Response> response) { - return new HTTPCURLRequest(this, resource, callback, loop_, response); + return new HTTPCURLRequest(this, resource, callback, response); } CURL *HTTPCURLContext::getHandle() { @@ -233,51 +181,45 @@ void HTTPCURLContext::checkMultiInfo() { } } -void HTTPCURLContext::perform(uv_poll_t *req, int /* status */, int events) { - assert(req->data); - auto socket = reinterpret_cast<Socket *>(req->data); - auto context = socket->context; - MBGL_VERIFY_THREAD(context->tid); +void HTTPCURLContext::perform(curl_socket_t s, util::RunLoop::Event events) { + MBGL_VERIFY_THREAD(tid); int flags = 0; - if (events & UV_READABLE) { + if (events == util::RunLoop::Event::Read) { flags |= CURL_CSELECT_IN; } - if (events & UV_WRITABLE) { + if (events == util::RunLoop::Event::Write) { flags |= CURL_CSELECT_OUT; } int running_handles = 0; - curl_multi_socket_action(context->multi, socket->sockfd, flags, &running_handles); - context->checkMultiInfo(); + curl_multi_socket_action(multi, s, flags, &running_handles); + checkMultiInfo(); } int HTTPCURLContext::handleSocket(CURL * /* handle */, curl_socket_t s, int action, void *userp, - void *socketp) { - auto socket = reinterpret_cast<Socket *>(socketp); + void * /* socketp */) { assert(userp); auto context = reinterpret_cast<HTTPCURLContext *>(userp); MBGL_VERIFY_THREAD(context->tid); - if (!socket && action != CURL_POLL_REMOVE) { - socket = new Socket(context, s); - curl_multi_assign(context->multi, s, (void *)socket); - } - switch (action) { - case CURL_POLL_IN: - socket->start(UV_READABLE, perform); + case CURL_POLL_IN: { + using namespace std::placeholders; + util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Read, + std::bind(&HTTPCURLContext::perform, context, _1, _2)); break; - case CURL_POLL_OUT: - socket->start(UV_WRITABLE, perform); + } + case CURL_POLL_OUT: { + using namespace std::placeholders; + util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Write, + std::bind(&HTTPCURLContext::perform, context, _1, _2)); break; + } case CURL_POLL_REMOVE: - if (socket) { - socket->stop(); - curl_multi_assign(context->multi, s, nullptr); - } + util::RunLoop::Get()->removeWatch(s); break; default: throw std::runtime_error("Unhandled CURL socket action"); @@ -286,9 +228,7 @@ int HTTPCURLContext::handleSocket(CURL * /* handle */, curl_socket_t s, int acti return 0; } -void HTTPCURLContext::onTimeout(UV_TIMER_PARAMS(req)) { - assert(req->data); - auto context = reinterpret_cast<HTTPCURLContext *>(req->data); +void HTTPCURLContext::onTimeout(HTTPCURLContext *context) { MBGL_VERIFY_THREAD(context->tid); int running_handles; CURLMcode error = curl_multi_socket_action(context->multi, CURL_SOCKET_TIMEOUT, 0, &running_handles); @@ -306,8 +246,10 @@ int HTTPCURLContext::startTimeout(CURLM * /* multi */, long timeout_ms, void *us // A timeout of 0 ms means that the timer will invoked in the next loop iteration. timeout_ms = 0; } - uv_timer_stop(context->timeout); - uv_timer_start(context->timeout, onTimeout, timeout_ms, 0); + context->timeout.stop(); + context->timeout.start(std::chrono::milliseconds(timeout_ms), Duration::zero(), + std::bind(&HTTPCURLContext::onTimeout, context)); + return 0; } @@ -411,7 +353,7 @@ static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm } #endif -HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_, uv_loop_t*, std::shared_ptr<const Response> response_) +HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_, std::shared_ptr<const Response> response_) : HTTPRequestBase(resource_, callback_), context(context_), existingResponse(response_), @@ -425,9 +367,9 @@ HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& reso if (!existingResponse->etag.empty()) { const std::string header = std::string("If-None-Match: ") + existingResponse->etag; headers = curl_slist_append(headers, header.c_str()); - } else if (existingResponse->modified) { + } else if (existingResponse->modified != Seconds::zero()) { const std::string time = - std::string("If-Modified-Since: ") + util::rfc1123(existingResponse->modified); + std::string("If-Modified-Since: ") + util::rfc1123(existingResponse->modified.count()); headers = curl_slist_append(headers, time.c_str()); } } @@ -525,7 +467,7 @@ size_t HTTPCURLRequest::headerCallback(char *const buffer, const size_t size, co // Always overwrite the modification date; We might already have a value here from the // Date header, but this one is more accurate. const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n - baton->response->modified = curl_getdate(value.c_str(), nullptr); + baton->response->modified = Seconds(curl_getdate(value.c_str(), nullptr)); } else if ((begin = headerMatches("etag: ", buffer, length)) != std::string::npos) { baton->response->etag = { buffer + begin, length - begin - 2 }; // remove \r\n } else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) { @@ -533,7 +475,7 @@ size_t HTTPCURLRequest::headerCallback(char *const buffer, const size_t size, co baton->response->expires = parseCacheControl(value.c_str()); } else if ((begin = headerMatches("expires: ", buffer, length)) != std::string::npos) { const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n - baton->response->expires = curl_getdate(value.c_str(), nullptr); + baton->response->expires = Seconds(curl_getdate(value.c_str(), nullptr)); } return length; @@ -620,8 +562,8 @@ void HTTPCURLRequest::handleResult(CURLcode code) { delete this; } -std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext(uv_loop_t* loop) { - return std::make_unique<HTTPCURLContext>(loop); +std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() { + return std::make_unique<HTTPCURLContext>(); } } // namespace mbgl diff --git a/platform/default/log_stderr.cpp b/platform/default/log_stderr.cpp index f7ef341845e..536841617a7 100644 --- a/platform/default/log_stderr.cpp +++ b/platform/default/log_stderr.cpp @@ -8,4 +8,4 @@ void Log::platformRecord(EventSeverity severity, const std::string &msg) { std::cerr << "[" << severity << "] " << msg << std::endl; } -} +} // namespace mbgl diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp new file mode 100644 index 00000000000..a5de3059984 --- /dev/null +++ b/platform/default/run_loop.cpp @@ -0,0 +1,207 @@ +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/async_task.hpp> +#include <mbgl/util/thread_local.hpp> + +#include <uv.h> + +#include <cassert> +#include <functional> +#include <unordered_map> + +namespace { + +using namespace mbgl::util; +static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>; + +} // namespace + +namespace mbgl { +namespace util { + +struct Watch { + static void onEvent(uv_poll_t* poll, int, int event) { + auto watch = reinterpret_cast<Watch*>(poll->data); + + RunLoop::Event watchEvent = RunLoop::Event::None; + switch (event) { + case UV_READABLE: + watchEvent = RunLoop::Event::Read; + break; + case UV_WRITABLE: + watchEvent = RunLoop::Event::Write; + break; + case UV_READABLE | UV_WRITABLE: + watchEvent = RunLoop::Event::ReadWrite; + break; + } + + watch->eventCallback(watch->fd, watchEvent); + }; + + static void onClose(uv_handle_t *poll) { + auto watch = reinterpret_cast<Watch*>(poll->data); + watch->closeCallback(); + }; + + uv_poll_t poll; + int fd; + + std::function<void(int, RunLoop::Event)> eventCallback; + std::function<void()> closeCallback; +}; + +RunLoop* RunLoop::Get() { + return current.get(); +} + +class RunLoop::Impl { +public: + Impl() = default; + + uv_loop_t *loop; + RunLoop::Type type; + std::unique_ptr<AsyncTask> async; + + std::unordered_map<int, std::unique_ptr<Watch>> watchPoll; +}; + +RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) { + switch (type) { + case Type::New: +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + impl->loop = uv_loop_new(); + if (impl->loop == nullptr) { +#else + impl->loop = new uv_loop_t; + if (uv_loop_init(impl->loop) != 0) { +#endif + throw std::runtime_error("Failed to initialize loop."); + } + break; + case Type::Default: + impl->loop = uv_default_loop(); + break; + } + + impl->type = type; + + current.set(this); + impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this)); +} + +RunLoop::~RunLoop() { + current.set(nullptr); + + if (impl->type == Type::Default) { + return; + } + + // Run the loop again to ensure that async + // close callbacks have been called. Not needed + // for the default main loop because it is only + // closed when the application exits. + impl->async.reset(); + runOnce(); + +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + uv_loop_delete(impl->loop); +#else + if (uv_loop_close(impl->loop) == UV_EBUSY) { + throw std::runtime_error("Failed to close loop."); + } + delete impl->loop; +#endif +} + +LOOP_HANDLE RunLoop::getLoopHandle() { + return current.get()->impl->loop; +} + +void RunLoop::push(std::shared_ptr<WorkTask> task) { + withMutex([&] { queue.push(task); }); + impl->async->send(); +} + +void RunLoop::run() { + MBGL_VERIFY_THREAD(tid); + + uv_run(impl->loop, UV_RUN_DEFAULT); +} + +void RunLoop::runOnce() { + MBGL_VERIFY_THREAD(tid); + + uv_run(impl->loop, UV_RUN_ONCE); +} + +void RunLoop::stop() { + invoke([&] { impl->async->unref(); }); +} + +void RunLoop::addWatch(int fd, Event event, std::function<void(int, Event)>&& callback) { + MBGL_VERIFY_THREAD(tid); + + Watch *watch = nullptr; + auto watchPollIter = impl->watchPoll.find(fd); + + if (watchPollIter == impl->watchPoll.end()) { + std::unique_ptr<Watch> watchPtr = std::make_unique<Watch>(); + + watch = watchPtr.get(); + impl->watchPoll[fd] = std::move(watchPtr); + + if (uv_poll_init(impl->loop, &watch->poll, fd)) { + throw std::runtime_error("Failed to init poll on file descriptor."); + } + } else { + watch = watchPollIter->second.get(); + } + + watch->poll.data = watch; + watch->fd = fd; + watch->eventCallback = std::move(callback); + + int pollEvent = 0; + switch (event) { + case Event::Read: + pollEvent = UV_READABLE; + break; + case Event::Write: + pollEvent = UV_WRITABLE; + break; + case Event::ReadWrite: + pollEvent = UV_READABLE | UV_WRITABLE; + break; + default: + throw std::runtime_error("Unhandled event."); + } + + if (uv_poll_start(&watch->poll, pollEvent, &Watch::onEvent)) { + throw std::runtime_error("Failed to start poll on file descriptor."); + } +} + +void RunLoop::removeWatch(int fd) { + MBGL_VERIFY_THREAD(tid); + + auto watchPollIter = impl->watchPoll.find(fd); + if (watchPollIter == impl->watchPoll.end()) { + return; + } + + Watch* watch = watchPollIter->second.release(); + impl->watchPoll.erase(watchPollIter); + + watch->closeCallback = [watch] { + delete watch; + }; + + if (uv_poll_stop(&watch->poll)) { + throw std::runtime_error("Failed to stop poll on file descriptor."); + } + + uv_close(reinterpret_cast<uv_handle_t*>(&watch->poll), &Watch::onClose); +} + +} // namespace util +} // namespace mbgl diff --git a/platform/default/settings_json.cpp b/platform/default/settings_json.cpp index 8c1c8ff7391..2c1bb3d2428 100644 --- a/platform/default/settings_json.cpp +++ b/platform/default/settings_json.cpp @@ -35,5 +35,5 @@ void Settings_JSON::clear() { zoom = 0; bearing = 0; pitch = 0; - debug = false; + debug = 0; } diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 41e5814a700..742c7e3217d 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -19,7 +19,7 @@ const static bool sqliteVersionCheck = []() { return true; }(); -#pragma GCC diagnostic pop +#pragma GCC diagnostic pop namespace mapbox { namespace sqlite { @@ -27,9 +27,9 @@ namespace sqlite { Database::Database(const std::string &filename, int flags) { const int err = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr); if (err != SQLITE_OK) { - Exception ex { err, sqlite3_errmsg(db) }; + const auto message = sqlite3_errmsg(db); db = nullptr; - throw ex; + throw Exception { err, message }; } } @@ -59,9 +59,9 @@ void Database::exec(const std::string &sql) { char *msg = nullptr; const int err = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &msg); if (msg) { - Exception ex { err, msg }; + const std::string message = msg; sqlite3_free(msg); - throw ex; + throw Exception { err, message }; } else if (err != SQLITE_OK) { throw Exception { err, sqlite3_errmsg(db) }; } @@ -69,7 +69,7 @@ void Database::exec(const std::string &sql) { Statement Database::prepare(const char *query) { assert(db); - return std::move(Statement(db, query)); + return Statement(db, query); } Statement::Statement(sqlite3 *db, const char *sql) { @@ -184,5 +184,5 @@ void Statement::reset() { sqlite3_reset(stmt); } -} -} +} // namespace sqlite +} // namespace mapbox diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 3e324f7ce16..1cad56f918a 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -21,6 +21,7 @@ enum OpenFlag : int { struct Exception : std::runtime_error { inline Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {} + inline Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {} const int code = 0; }; diff --git a/platform/default/sqlite_cache.cpp b/platform/default/sqlite_cache.cpp index 126e915e9b7..43b67cfd7f0 100644 --- a/platform/default/sqlite_cache.cpp +++ b/platform/default/sqlite_cache.cpp @@ -118,12 +118,12 @@ void SQLiteCache::Impl::get(const Resource &resource, Callback callback) { // Status codes > 1 indicate an error response->error = std::make_unique<Response::Error>(Response::Error::Reason(status)); } - response->modified = getStmt->get<int64_t>(1); + response->modified = Seconds(getStmt->get<Seconds::rep>(1)); response->etag = getStmt->get<std::string>(2); - response->expires = getStmt->get<int64_t>(3); - response->data = std::make_shared<std::string>(std::move(getStmt->get<std::string>(4))); + response->expires = Seconds(getStmt->get<Seconds::rep>(3)); + response->data = std::make_shared<std::string>(getStmt->get<std::string>(4)); if (getStmt->get<int>(5)) { // == compressed - response->data = std::make_shared<std::string>(std::move(util::decompress(*response->data))); + response->data = std::make_shared<std::string>(util::decompress(*response->data)); } callback(std::move(response)); } else { @@ -177,9 +177,9 @@ void SQLiteCache::Impl::put(const Resource& resource, std::shared_ptr<const Resp putStmt->bind(2 /* status */, 1 /* success */); } putStmt->bind(3 /* kind */, int(resource.kind)); - putStmt->bind(4 /* modified */, response->modified); + putStmt->bind(4 /* modified */, response->modified.count()); putStmt->bind(5 /* etag */, response->etag.c_str()); - putStmt->bind(6 /* expires */, response->expires); + putStmt->bind(6 /* expires */, response->expires.count()); std::string data; if (resource.kind != Resource::SpriteImage && response->data) { @@ -208,7 +208,7 @@ void SQLiteCache::Impl::put(const Resource& resource, std::shared_ptr<const Resp } } -void SQLiteCache::Impl::refresh(const Resource& resource, int64_t expires) { +void SQLiteCache::Impl::refresh(const Resource& resource, Seconds expires) { try { if (!db) { createDatabase(); @@ -226,7 +226,7 @@ void SQLiteCache::Impl::refresh(const Resource& resource, int64_t expires) { } const auto canonicalURL = util::mapbox::canonicalURL(resource.url); - refreshStmt->bind(1, int64_t(expires)); + refreshStmt->bind(1, expires.count()); refreshStmt->bind(2, canonicalURL.c_str()); refreshStmt->run(); } catch (mapbox::sqlite::Exception& ex) { @@ -246,4 +246,4 @@ std::shared_ptr<SQLiteCache> SharedSQLiteCache::get(const std::string &path) { std::weak_ptr<SQLiteCache> SharedSQLiteCache::masterPtr; -} +} // namespace mbgl diff --git a/platform/default/sqlite_cache_impl.hpp b/platform/default/sqlite_cache_impl.hpp index 700771f2e8c..f557666e3f1 100644 --- a/platform/default/sqlite_cache_impl.hpp +++ b/platform/default/sqlite_cache_impl.hpp @@ -2,6 +2,7 @@ #define MBGL_STORAGE_DEFAULT_SQLITE_CACHE_IMPL #include <mbgl/storage/sqlite_cache.hpp> +#include <mbgl/util/chrono.hpp> namespace mapbox { namespace sqlite { @@ -19,7 +20,7 @@ class SQLiteCache::Impl { void get(const Resource&, Callback); void put(const Resource& resource, std::shared_ptr<const Response> response); - void refresh(const Resource& resource, int64_t expires); + void refresh(const Resource& resource, Seconds expires); private: void createDatabase(); diff --git a/platform/default/timer.cpp b/platform/default/timer.cpp new file mode 100644 index 00000000000..774c1bb97ef --- /dev/null +++ b/platform/default/timer.cpp @@ -0,0 +1,86 @@ +#include <mbgl/util/timer.hpp> + +#include <mbgl/util/run_loop.hpp> + +#include <uv.h> + +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 +#define UV_TIMER_PARAMS(timer) uv_timer_t *timer, int +#else +#define UV_TIMER_PARAMS(timer) uv_timer_t *timer +#endif + +namespace mbgl { +namespace util { + +class Timer::Impl { +public: + Impl() : timer(new uv_timer_t) { + uv_loop_t* loop = reinterpret_cast<uv_loop_t*>(RunLoop::getLoopHandle()); + if (uv_timer_init(loop, timer) != 0) { + throw std::runtime_error("Failed to initialize timer."); + } + + handle()->data = this; + } + + ~Impl() { + uv_close(handle(), [](uv_handle_t* h) { + delete reinterpret_cast<uv_timer_t*>(h); + }); + } + + void start(uint64_t timeout, uint64_t repeat, std::function<void ()>&& cb_) { + cb = std::move(cb_); + if (uv_timer_start(timer, timerCallback, timeout, repeat) != 0) { + throw std::runtime_error("Failed to start timer."); + } + } + + void stop() { + cb = nullptr; + if (uv_timer_stop(timer) != 0) { + throw std::runtime_error("Failed to stop timer."); + } + } + + void unref() { + uv_unref(handle()); + } + +private: + static void timerCallback(UV_TIMER_PARAMS(handle)) { + reinterpret_cast<Impl*>(handle->data)->cb(); + } + + uv_handle_t* handle() { + return reinterpret_cast<uv_handle_t*>(timer); + } + + uv_timer_t* timer; + + std::function<void()> cb; +}; + +Timer::Timer() + : impl(std::make_unique<Impl>()) { +} + +Timer::~Timer() = default; + +void Timer::start(Duration timeout, Duration repeat, std::function<void()>&& cb) { + impl->start(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count(), + std::chrono::duration_cast<std::chrono::milliseconds>(repeat).count(), + std::move(cb)); +} + +void Timer::stop() { + impl->stop(); +} + +void Timer::unref() { + impl->unref(); +} + +} // namespace util +} // namespace mbgl diff --git a/platform/default/uv_zip.c b/platform/default/uv_zip.c deleted file mode 100644 index 3019477eb89..00000000000 --- a/platform/default/uv_zip.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "uv_zip.h" - -#include <assert.h> -#include <errno.h> -#include <string.h> - - -void uv__zip_open_error(uv_zip_t *zip, int error) { - assert(zip); - zip->result = -error; - if (zip->message) { - free((char *)zip->message); - zip->message = NULL; - } - const zip_uint64_t size = zip_error_to_str(NULL, 0, error, errno) + 1; - zip->message = malloc(size); - zip_error_to_str((char *)zip->message, size, error, errno); -} - -void uv__zip_store_error(uv_zip_t *zip, const char *message) { - assert(zip); - if (zip->message) { - free((char *)zip->message); - zip->message = NULL; - } - const unsigned long length = strlen(message) + 1; - zip->message = malloc(length); - strncpy((char *)zip->message, message, length); -} - -void uv__zip_error(uv_zip_t *zip) { - assert(zip); - int error; - zip_error_get(zip->archive, &error, NULL); - zip->result = -error; - uv__zip_store_error(zip, zip_strerror(zip->archive)); -} - -void uv__zip_file_error(uv_zip_t *zip) { - assert(zip); - int error; - zip_file_error_get(zip->file, &error, NULL); - zip->result = -error; - uv__zip_store_error(zip, zip_file_strerror(zip->file)); -} - -void uv__zip_work_fdopen(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(!zip->archive); - - // extract the fd - uv_file fd = *(uv_file *)zip->path; - free((uv_file *)zip->path); - zip->path = NULL; - - int error; - zip->archive = zip_fdopen(fd, zip->flags, &error); - if (!zip->archive) { - uv__zip_open_error(zip, error); - } else { - zip->result = 0; - } -} - -void uv__zip_work_stat(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(zip->archive); - if (!zip->stat) { - zip->stat = malloc(sizeof(struct zip_stat)); - zip_stat_init(zip->stat); - } - if (0 != zip_stat(zip->archive, zip->path, zip->flags, zip->stat)) { - uv__zip_error(zip); - } -} - -void uv__zip_work_fopen(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(zip->archive); - zip->file = zip_fopen(zip->archive, zip->path, zip->flags); - if (!zip->file) { - uv__zip_error(zip); - } -} - -void uv__zip_work_fread(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(zip->file); - assert(zip->buf); - zip->result = zip_fread(zip->file, zip->buf->base, zip->buf->len); - if (zip->result < 0) { - uv__zip_file_error(zip); - } -} - -void uv__zip_work_fclose(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(zip->file); - zip->result = zip_fclose(zip->file); - if (zip->result != 0) { - uv__zip_file_error(zip); - } else { - zip->file = NULL; - } -} - -void uv__zip_work_discard(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip->archive); - zip_discard(zip->archive); - zip->archive = NULL; - zip->result = 0; -} - -void uv__zip_after_work(uv_work_t *req, int status) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - if (zip->cb) { - zip->cb(zip); - } -} - -void uv_zip_init(uv_zip_t *zip) { - assert(zip); - zip->work.data = zip; - zip->message = NULL; - zip->stat = NULL; - uv_zip_cleanup(zip); -} - -void uv_zip_cleanup(uv_zip_t *zip) { - assert(zip); - zip->data = NULL; - zip->flags = 0; - zip->result = 0; - zip->path = NULL; - zip->cb = NULL; - zip->archive = NULL; - zip->file = NULL; - zip->buf = NULL; - - if (zip->message) { - free((char *)zip->message); - zip->message = NULL; - } - - if (zip->stat) { - free(zip->stat); - zip->stat = NULL; - } -} - -int uv_zip_fdopen(uv_loop_t* loop, uv_zip_t *zip, uv_file fd, int flags, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(fd); - zip->result = 0; - zip->path = malloc(sizeof(uv_file)); - *(uv_file *)zip->path = fd; - zip->flags = flags; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_fdopen, uv__zip_after_work); -} - -int uv_zip_stat(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(fname); - assert(strlen(fname)); - assert(zip->archive); - zip->result = 0; - zip->path = fname; - zip->flags = flags; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_stat, uv__zip_after_work); -} - -int uv_zip_fopen(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(fname); - assert(strlen(fname)); - assert(zip->archive); - zip->result = 0; - zip->path = fname; - zip->flags = flags; - zip->file = NULL; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_fopen, uv__zip_after_work); -} - -int uv_zip_fclose(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(file); - assert(zip->archive); - zip->result = 0; - zip->file = file; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_fclose, uv__zip_after_work); -} - -int uv_zip_fread(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_buf_t *buf, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(file); - assert(buf); - assert(zip->archive); - zip->result = 0; - zip->file = file; - zip->buf = buf; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_fread, uv__zip_after_work); -} - -int uv_zip_discard(uv_loop_t* loop, uv_zip_t *zip, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(!zip->file); - zip->result = 0; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_discard, uv__zip_after_work); -} diff --git a/platform/default/uv_zip.h b/platform/default/uv_zip.h deleted file mode 100644 index 869a415f21b..00000000000 --- a/platform/default/uv_zip.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef UV_ZIP -#define UV_ZIP - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdlib.h> -#include <uv.h> -#include <zip.h> - -typedef struct uv_zip_s uv_zip_t; - -typedef void (*uv_zip_cb)(uv_zip_t* req); - -struct uv_zip_s { - uv_work_t work; - ssize_t result; - const char *message; - struct zip *archive; - struct zip_file *file; - struct zip_stat *stat; - void *data; - zip_flags_t flags; - const char *path; - uv_zip_cb cb; - uv_buf_t *buf; -}; - -void uv_zip_init(uv_zip_t *zip); -void uv_zip_cleanup(uv_zip_t *zip); - -int uv_zip_fdopen(uv_loop_t* loop, uv_zip_t *zip, uv_file fd, int flags, uv_zip_cb cb); -int uv_zip_stat(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb); -int uv_zip_fopen(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb); -int uv_zip_fread(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_buf_t *buf, uv_zip_cb cb); -int uv_zip_fclose(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_zip_cb cb); -int uv_zip_discard(uv_loop_t* loop, uv_zip_t *zip, uv_zip_cb cb); - -#ifdef __cplusplus -} -#endif - -#endif // UV_ZIP diff --git a/platform/ios/MGLAccountManager.m b/platform/ios/MGLAccountManager.m index b78db7094db..edc5c463f6a 100644 --- a/platform/ios/MGLAccountManager.m +++ b/platform/ios/MGLAccountManager.m @@ -18,6 +18,9 @@ @implementation MGLAccountManager #pragma mark - Internal + (void)load { + // Load all referenced categories due to absence of -ObjC linker flag + [MGLCategoryLoader loadCategories]; + // Read the initial configuration from Info.plist. The shown-in-app setting // preempts the Settings bundle check in -[MGLMapboxEvents init] triggered // by setting the access token. @@ -35,8 +38,6 @@ + (void)load { // Can be called from any thread. // + (instancetype) sharedManager { - [MGLCategoryLoader loadCategories]; - if (NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) { return nil; } @@ -85,7 +86,7 @@ + (NSString *)bundleIdentifier { } + (NSString *)kitDisplayVersion { - return @"3.0.0-pre.7"; + return @"3.0.0"; } + (void)initializeIfNeeded { diff --git a/platform/ios/MGLAnnotationImage.m b/platform/ios/MGLAnnotationImage.m index cd211c52e61..ad560552ce7 100644 --- a/platform/ios/MGLAnnotationImage.m +++ b/platform/ios/MGLAnnotationImage.m @@ -1,20 +1,20 @@ -#import "MGLAnnotationImage.h" +#import "MGLAnnotationImage_Private.h" @interface MGLAnnotationImage () -@property (nonatomic) UIImage *image; -@property (nonatomic) NSString *reuseIdentifier; +@property (nonatomic, strong) NSString *reuseIdentifier; +@property (nonatomic, weak) id<MGLAnnotationImageDelegate> delegate; @end @implementation MGLAnnotationImage -+ (instancetype)annotationImageWithImage:(UIImage *)image reuseIdentifier:(NSString *)reuseIdentifier ++ (instancetype)annotationImageWithImage:(UIImage *)image reuseIdentifier:(nullable NSString *)reuseIdentifier { return [[self alloc] initWithImage:image reuseIdentifier:reuseIdentifier]; } -- (instancetype)initWithImage:(UIImage *)image reuseIdentifier:(NSString *)reuseIdentifier +- (instancetype)initWithImage:(UIImage *)image reuseIdentifier:(nullable NSString *)reuseIdentifier { self = [super init]; @@ -28,4 +28,9 @@ - (instancetype)initWithImage:(UIImage *)image reuseIdentifier:(NSString *)reuse return self; } +- (void)setImage:(UIImage *)image { + _image = image; + [self.delegate annotationImageNeedsRedisplay:self]; +} + @end diff --git a/platform/ios/MGLAnnotationImage_Private.h b/platform/ios/MGLAnnotationImage_Private.h new file mode 100644 index 00000000000..f22a9ac4e20 --- /dev/null +++ b/platform/ios/MGLAnnotationImage_Private.h @@ -0,0 +1,18 @@ +#import "MGLAnnotationImage.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol MGLAnnotationImageDelegate <NSObject> + +@required +- (void)annotationImageNeedsRedisplay:(MGLAnnotationImage *)annotationImage; + +@end + +@interface MGLAnnotationImage (Private) + +@property (nonatomic, weak) id<MGLAnnotationImageDelegate> delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/ios/MGLCategoryLoader.m b/platform/ios/MGLCategoryLoader.m index 13d82b19b2e..50309436b75 100644 --- a/platform/ios/MGLCategoryLoader.m +++ b/platform/ios/MGLCategoryLoader.m @@ -4,15 +4,21 @@ #import "NSProcessInfo+MGLAdditions.h" #import "NSString+MGLAdditions.h" -// https://github.com/mapbox/mapbox-gl-native/issues/2966 +#import "MGLMapView.h" @implementation MGLCategoryLoader + (void)loadCategories { + // https://github.com/mapbox/mapbox-gl-native/issues/2966 + // mgl_linkBundleCategory(); mgl_linkProcessCategory(); mgl_linkStringCategory(); + + // https://github.com/mapbox/mapbox-gl-native/issues/3113 + // + NSString *description = [MGLMapView description]; } @end diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index c923adb1601..fc5e3904530 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -12,6 +12,7 @@ #include <mbgl/annotation/shape_annotation.hpp> #include <mbgl/sprite/sprite_image.hpp> #include <mbgl/map/camera.hpp> +#include <mbgl/map/mode.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/platform/darwin/reachability.h> #include <mbgl/storage/sqlite_cache.hpp> @@ -33,6 +34,7 @@ #import "MGLUserLocationAnnotationView.h" #import "MGLUserLocation_Private.h" #import "MGLAccountManager_Private.h" +#import "MGLAnnotationImage_Private.h" #import "MGLMapboxEvents.h" #import "SMCalloutView.h" @@ -77,7 +79,8 @@ @interface MGLMapView () <UIGestureRecognizerDelegate, GLKViewDelegate, CLLocationManagerDelegate, UIActionSheetDelegate, - SMCalloutViewDelegate> + SMCalloutViewDelegate, + MGLAnnotationImageDelegate> @property (nonatomic) EAGLContext *context; @property (nonatomic) GLKView *glView; @@ -747,7 +750,7 @@ - (void)sleepGL:(__unused NSNotification *)notification self.glSnapshotView.image = self.glView.snapshot; self.glSnapshotView.hidden = NO; - if (_mbglMap->getDebug() && [self.glSnapshotView.subviews count] == 0) + if (_mbglMap->getDebug() != mbgl::MapDebugOptions::NoDebug && [self.glSnapshotView.subviews count] == 0) { UIView *snapshotTint = [[UIView alloc] initWithFrame:self.glSnapshotView.bounds]; snapshotTint.autoresizingMask = self.glSnapshotView.autoresizingMask; @@ -1502,13 +1505,15 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N - (void)setDebugActive:(BOOL)debugActive { - _mbglMap->setDebug(debugActive); - _mbglMap->setCollisionDebug(debugActive); + _mbglMap->setDebug(debugActive ? mbgl::MapDebugOptions::TileBorders + | mbgl::MapDebugOptions::ParseStatus + | mbgl::MapDebugOptions::Collision + : mbgl::MapDebugOptions::NoDebug); } - (BOOL)isDebugActive { - return (_mbglMap->getDebug() || _mbglMap->getCollisionDebug()); + return (_mbglMap->getDebug() != mbgl::MapDebugOptions::NoDebug); } - (void)resetNorth @@ -1527,10 +1532,9 @@ - (void)resetPosition _mbglMap->resetPosition(); } -- (void)toggleDebug +- (void)cycleDebugOptions { - _mbglMap->toggleDebug(); - _mbglMap->toggleCollisionDebug(); + _mbglMap->cycleDebugOptions(); } - (void)emptyMemoryCache @@ -2202,6 +2206,7 @@ - (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations [self.annotationImages setObject:annotationImage forKey:annotationImage.reuseIdentifier]; [self installAnnotationImage:annotationImage]; + annotationImage.delegate = self; } NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier]; @@ -2261,7 +2266,7 @@ - (void)installAnnotationImage:(MGLAnnotationImage *)annotationImage // sprite upload NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier]; - _mbglMap->setSprite(symbolName.UTF8String, cSpriteImage); + _mbglMap->addAnnotationIcon(symbolName.UTF8String, cSpriteImage); } - (void)removeAnnotation:(id <MGLAnnotation>)annotation @@ -2395,7 +2400,7 @@ - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated // determine anchor point based on symbol CGPoint calloutAnchorPoint = [self convertCoordinate:annotation.coordinate toPointToView:self]; - double y = _mbglMap->getTopOffsetPixelsForAnnotationSymbol(cSymbolName); + double y = _mbglMap->getTopOffsetPixelsForAnnotationIcon(cSymbolName); calloutBounds = CGRectMake(calloutAnchorPoint.x - 1, calloutAnchorPoint.y + y, 0, 0); } @@ -2501,6 +2506,17 @@ - (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated: animated:animated]; } +#pragma mark Annotation Image Delegate + +- (void)annotationImageNeedsRedisplay:(MGLAnnotationImage *)annotationImage +{ + // remove sprite + NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier]; + _mbglMap->removeAnnotationIcon(symbolName.UTF8String); + [self installAnnotationImage:annotationImage]; + _mbglMap->update(mbgl::Update::Annotations); +} + #pragma mark - User Location - - (void)setShowsUserLocation:(BOOL)showsUserLocation diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m index a0f87eeabe6..259cfb3defb 100644 --- a/platform/ios/MGLMapboxEvents.m +++ b/platform/ios/MGLMapboxEvents.m @@ -75,22 +75,6 @@ - (instancetype)init { } else { _scale = [UIScreen mainScreen].scale; } - - // Collect cellular carrier data if CoreTelephony is linked - Class CTTelephonyNetworkInfo = NSClassFromString(@"CTTelephonyNetworkInfo"); - if (CTTelephonyNetworkInfo != NULL) { - id telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init]; - - SEL subscriberCellularProviderSelector = NSSelectorFromString(@"subscriberCellularProvider"); - id carrierVendor = ((id (*)(id, SEL))[telephonyNetworkInfo methodForSelector:subscriberCellularProviderSelector])(telephonyNetworkInfo, subscriberCellularProviderSelector); - - // Guard against simulator, iPod Touch, etc. - if (carrierVendor) { - SEL carrierNameSelector = NSSelectorFromString(@"carrierName"); - NSString *carrierName = ((id (*)(id, SEL))[carrierVendor methodForSelector:carrierNameSelector])(carrierVendor, carrierNameSelector); - _carrier = carrierName; - } - } } return self; } @@ -509,16 +493,11 @@ - (void) pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *) [evt setValue:@((int)(100 * [UIDevice currentDevice].batteryLevel)) forKey:@"batteryLevel"]; [evt setValue:@(strongSelf.data.scale) forKey:@"resolution"]; - if (strongSelf.data.carrier) { - [evt setValue:strongSelf.data.carrier forKey:@"carrier"]; - - NSString *cell = [strongSelf currentCellularNetworkConnectionType]; - [evt setObject:(cell ? cell : [NSNull null]) forKey:@"cellularNetworkType"]; - } - MGLReachability *reachability = [MGLReachability reachabilityForLocalWiFi]; [evt setValue:([reachability isReachableViaWiFi] ? @YES : @NO) forKey:@"wifi"]; - + + [evt setValue:[strongSelf applicationState] forKey:@"applicationState"]; + [evt setValue:@([strongSelf contentSizeScale]) forKey:@"accessibilityFontScale"]; // Make Immutable Version @@ -676,6 +655,41 @@ - (NSString *) deviceOrientation { return result; } +// Can be called from any thread. +// +- (NSString *) applicationState { + __block NSString *result; + + NSString *(^applicationStateBlock)(void) = ^{ + switch ([UIApplication sharedApplication].applicationState) { + case UIApplicationStateActive: + result = @"Active"; + break; + case UIApplicationStateInactive: + result = @"Inactive"; + break; + case UIApplicationStateBackground: + result = @"Background"; + break; + default: + result = @"Default - Unknown"; + break; + } + + return result; + }; + + if ( ! [[NSThread currentThread] isMainThread]) { + dispatch_sync(dispatch_get_main_queue(), ^{ + result = applicationStateBlock(); + }); + } else { + result = applicationStateBlock(); + } + + return result; +} + // Can be called from any thread. // - (NSInteger) contentSizeScale { @@ -724,27 +738,6 @@ - (NSInteger) contentSizeScale { return result; } -// Can be called from any thread. -// -- (NSString *) currentCellularNetworkConnectionType { - NSString *radioTech; - - Class CTTelephonyNetworkInfo = NSClassFromString(@"CTTelephonyNetworkInfo"); - if (CTTelephonyNetworkInfo) { - id telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init]; - SEL currentRadioAccessTechnologySelector = NSSelectorFromString(@"currentRadioAccessTechnology"); - radioTech = ((id (*)(id, SEL))[telephonyNetworkInfo methodForSelector:currentRadioAccessTechnologySelector])(telephonyNetworkInfo, currentRadioAccessTechnologySelector); - } - - if (radioTech == nil) { - return nil; - } else if ([radioTech hasPrefix:@"CTRadioAccessTechnology"]) { - return [radioTech substringFromIndex:23]; - } else { - return @"Unknown"; - } -} - // Can be called from any thread. // + (BOOL) checkPushEnabled { diff --git a/platform/node/src/node_log.hpp b/platform/node/src/node_log.hpp index 8798016d7ab..d29e4e28e0b 100644 --- a/platform/node/src/node_log.hpp +++ b/platform/node/src/node_log.hpp @@ -5,7 +5,6 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wnested-anon-types" #include <nan.h> #pragma GCC diagnostic pop diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index d4c551d666a..d9ff63f720a 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -405,8 +405,8 @@ void NodeMap::release() { valid = false; - uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *handle) { - delete reinterpret_cast<uv_async_t *>(handle); + uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *h) { + delete reinterpret_cast<uv_async_t *>(h); }); map.reset(nullptr); @@ -433,8 +433,8 @@ NodeMap::NodeMap(v8::Local<v8::Object> options) : async(new uv_async_t) { async->data = this; - uv_async_init(uv_default_loop(), async, [](UV_ASYNC_PARAMS(handle)) { - reinterpret_cast<NodeMap *>(handle->data)->renderFinished(); + uv_async_init(uv_default_loop(), async, [](UV_ASYNC_PARAMS(h)) { + reinterpret_cast<NodeMap *>(h->data)->renderFinished(); }); // Make sure the async handle doesn't keep the loop alive. diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index 23cd3c9fab3..2c1cbd25458 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -7,7 +7,6 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wnested-anon-types" #include <nan.h> #pragma GCC diagnostic pop diff --git a/platform/node/src/node_mapbox_gl_native.cpp b/platform/node/src/node_mapbox_gl_native.cpp index 76c522d83e4..e0b094d5705 100644 --- a/platform/node/src/node_mapbox_gl_native.cpp +++ b/platform/node/src/node_mapbox_gl_native.cpp @@ -1,7 +1,6 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wnested-anon-types" #include <node.h> #include <nan.h> #pragma GCC diagnostic pop @@ -14,7 +13,7 @@ namespace node_mbgl { mbgl::util::RunLoop& NodeRunLoop() { - static mbgl::util::RunLoop nodeRunLoop(uv_default_loop()); + static mbgl::util::RunLoop nodeRunLoop; return nodeRunLoop; } diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp index 4575d42a7a9..dd8f2336cac 100644 --- a/platform/node/src/node_request.cpp +++ b/platform/node/src/node_request.cpp @@ -1,5 +1,6 @@ #include "node_request.hpp" #include <mbgl/storage/response.hpp> +#include <mbgl/util/chrono.hpp> #include <cmath> #include <iostream> @@ -43,6 +44,8 @@ v8::Handle<v8::Object> NodeRequest::Create(const mbgl::Resource& resource, mbgl: NAN_METHOD(NodeRequest::Respond) { using Error = mbgl::Response::Error; + using Milliseconds = mbgl::Milliseconds; + mbgl::Response response; if (info.Length() < 1) { @@ -63,14 +66,14 @@ NAN_METHOD(NodeRequest::Respond) { if (Nan::Has(res, Nan::New("modified").ToLocalChecked()).FromJust()) { const double modified = Nan::Get(res, Nan::New("modified").ToLocalChecked()).ToLocalChecked()->ToNumber()->Value(); if (!std::isnan(modified)) { - response.modified = modified / 1000; // JS timestamps are milliseconds + response.modified = mbgl::asSeconds(Milliseconds(Milliseconds::rep(modified))); } } if (Nan::Has(res, Nan::New("expires").ToLocalChecked()).FromJust()) { const double expires = Nan::Get(res, Nan::New("expires").ToLocalChecked()).ToLocalChecked()->ToNumber()->Value(); if (!std::isnan(expires)) { - response.expires = expires / 1000; // JS timestamps are milliseconds + response.expires = mbgl::asSeconds(Milliseconds(Milliseconds::rep(expires))); } } diff --git a/platform/node/src/node_request.hpp b/platform/node/src/node_request.hpp index 3a06a7b62f7..8e57cb30ec9 100644 --- a/platform/node/src/node_request.hpp +++ b/platform/node/src/node_request.hpp @@ -3,7 +3,6 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wnested-anon-types" #include <nan.h> #pragma GCC diagnostic pop diff --git a/scripts/clang-tidy.sh b/scripts/clang-tidy.sh new file mode 100755 index 00000000000..a0a0aa20e40 --- /dev/null +++ b/scripts/clang-tidy.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason" + +command -v ${CLANG_TIDY:-clang-tidy} >/dev/null 2>&1 || { + echo "Can't find ${CLANG_TIDY:-clang-tidy} in PATH." + if [ -z ${CLANG_TIDY} ]; then + echo "Alternatively, you can set CLANG_TIDY to point to clang-tidy." + fi + exit 1 +} + +cd build/${HOST}-${HOST_VERSION}/${BUILDTYPE} + +git ls-files '../../../src/mbgl/*.cpp' '../../../platform/*.cpp' '../../../test/*.cpp' | \ + xargs -I{} -P ${JOBS} ${CLANG_TIDY:-clang-tidy} -header-filter='\/mbgl\/' {} diff --git a/scripts/ios/package.sh b/scripts/ios/package.sh index 12087006984..41694c2f631 100755 --- a/scripts/ios/package.sh +++ b/scripts/ios/package.sh @@ -128,7 +128,7 @@ README=/tmp/mbgl/README.md cat ios/docs/pod-README.md > ${README} echo >> ${README} echo -n "#" >> ${README} -cat CHANGELOG.md >> ${README} +cat CHANGELOG.md | sed -n "/^## iOS ${DOCS_VERSION}/,/^##/p" | sed '$d' >> ${README} # Copy headers to a temporary location where we can substitute macros that appledoc doesn't understand. cp -r "${OUTPUT}/static/Headers" /tmp/mbgl perl \ diff --git a/scripts/linux/configure.sh b/scripts/linux/configure.sh index 6a88c13c01c..fb6b3b1bdc0 100644 --- a/scripts/linux/configure.sh +++ b/scripts/linux/configure.sh @@ -10,7 +10,6 @@ SQLITE_VERSION=3.9.1 LIBUV_VERSION=1.7.5 ZLIB_VERSION=system NUNICODE_VERSION=1.6 -LIBZIP_VERSION=1.0.1 GEOJSONVT_VERSION=2.1.6.3 VARIANT_VERSION=1.0 RAPIDJSON_VERSION=1.0.2 diff --git a/scripts/linux/tidy.sh b/scripts/linux/tidy.sh new file mode 100755 index 00000000000..424c82c3cd1 --- /dev/null +++ b/scripts/linux/tidy.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +# Ensure mason is on the PATH +export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason" + +BUILDTYPE=${BUILDTYPE:-Release} + +export CLANG_TIDY=clang-tidy-3.8 + +mapbox_time "config" \ +make config + +mapbox_time "tidy" \ +make tidy diff --git a/scripts/main.mk b/scripts/main.mk index b4a5f9894eb..aefd2690ed6 100644 --- a/scripts/main.mk +++ b/scripts/main.mk @@ -3,12 +3,12 @@ ifeq (,$(V)) endif # Determine host platform -HOST ?= $(BUILD) +export HOST ?= $(BUILD) # Defines host defaults include scripts/$(HOST)/defaults.mk -HOST_VERSION ?= $(BUILD_VERSION) +export HOST_VERSION ?= $(BUILD_VERSION) # Optionally include version-specific host defaults -include scripts/$(HOST)/$(HOST_VERSION)/defaults.mk @@ -20,7 +20,7 @@ ifneq (,$(wildcard scripts/$(HOST)/$(HOST_VERSION)/configure.sh)) CONFIGURE_FILES += scripts/$(HOST)/$(HOST_VERSION)/configure.sh endif -HOST_SLUG = $(HOST)-$(HOST_VERSION) +export HOST_SLUG = $(HOST)-$(HOST_VERSION) CONFIGURE_FILES = scripts/$(HOST)/configure.sh ifneq (,$(wildcard scripts/$(HOST)/$(HOST_VERSION)/configure.sh)) CONFIGURE_FILES += scripts/$(HOST)/$(HOST_VERSION)/configure.sh @@ -65,6 +65,8 @@ config/%.gypi: $(SUBMODULES) configure $(CONFIGURE_FILES) @printf "$(TEXT_BOLD)$(COLOR_GREEN)* Recreating project...$(FORMAT_END)\n" $(QUIET)$(ENV) ./scripts/flock.py build/Configure.lock ./configure config/$*.gypi +.PHONY: config +config: config/$(HOST_SLUG).gypi #### Build files ############################################################### @@ -94,6 +96,12 @@ Xcode/__project__: print-env $(SUBMODULES) config/$(HOST_SLUG).gypi $(QUIET)$(ENV) deps/run_gyp gyp/$(HOST).gyp $(GYP_FLAGS) \ -f xcode$(GYP_FLAVOR_SUFFIX) +.PHONY: Ninja/__project__ +Ninja/__project__: print-env $(SUBMODULES) config/$(HOST_SLUG).gypi + @printf "$(TEXT_BOLD)$(COLOR_GREEN)* Recreating project...$(FORMAT_END)\n" + $(QUIET)$(ENV) deps/run_gyp gyp/$(HOST).gyp -Gconfig=$(BUILDTYPE) $(GYP_FLAGS) \ + -f ninja + #### Build individual targets ################################################## NODE_PRE_GYP = $(shell npm bin)/node-pre-gyp @@ -136,6 +144,25 @@ Xcode/%: Xcode/__project__ -jobs $(JOBS) \ $(XCPRETTY) +Ninja/%: Ninja/__project__ + @printf "$(TEXT_BOLD)$(COLOR_GREEN)* Building target $*...$(FORMAT_END)\n" + $(QUIET)$(ENV) deps/ninja/ninja-$(HOST) -C build/$(HOST_SLUG)/$(BUILDTYPE) $* + + +Ninja/compdb: OUTPUT=build/$(HOST_SLUG)/$(BUILDTYPE)/compile_commands.json +Ninja/compdb: Ninja/__project__ + @printf "$(TEXT_BOLD)$(COLOR_GREEN)* Writing to $(OUTPUT)$(FORMAT_END)\n" + $(QUIET)$(ENV) deps/ninja/ninja-$(HOST) -C build/$(HOST_SLUG)/$(BUILDTYPE) \ + -t compdb cc cc_s cxx objc objcxx > $(OUTPUT) + +#### Tidy ###################################################################### + +tidy: Ninja/compdb + @printf "$(TEXT_BOLD)$(COLOR_GREEN)* Generating header files...$(FORMAT_END)\n" + $(QUIET)$(ENV) deps/ninja/ninja-$(HOST) -C build/$(HOST_SLUG)/$(BUILDTYPE) version shaders + @printf "$(TEXT_BOLD)$(COLOR_GREEN)* Running tidy...$(FORMAT_END)\n" + @./scripts/clang-tidy.sh + #### Run tests ################################################################# test-%: Makefile/test diff --git a/scripts/node/after_script.sh b/scripts/node/after_script.sh index 8673b079c8b..6ab07fe03d6 100755 --- a/scripts/node/after_script.sh +++ b/scripts/node/after_script.sh @@ -11,7 +11,7 @@ else fi COMMIT_MESSAGE=$(git show -s --format=%B $TRAVIS_COMMIT | tr -d '\n') -PACKAGE_JSON_VERSION=$(node ./scripts/node/package-version.js) +PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)") if [[ ${TRAVIS_TAG} == node-v${PACKAGE_JSON_VERSION} ]] || test "${COMMIT_MESSAGE#*'[publish binary]'}" != "$COMMIT_MESSAGE"; then source ~/.nvm/nvm.sh diff --git a/scripts/node/package-version.js b/scripts/node/package-version.js deleted file mode 100644 index 99b56aee44e..00000000000 --- a/scripts/node/package-version.js +++ /dev/null @@ -1,6 +0,0 @@ -var readJSON = require('read-package-json'); - -readJSON('package.json', function(err, data) { - if (err) process.stderr.write(err); - process.stdout.write(data.version); -}); diff --git a/scripts/osx/configure.sh b/scripts/osx/configure.sh index 823e3b62604..71bb2c360e2 100644 --- a/scripts/osx/configure.sh +++ b/scripts/osx/configure.sh @@ -10,7 +10,6 @@ SQLITE_VERSION=3.9.1 LIBUV_VERSION=1.7.5 ZLIB_VERSION=system NUNICODE_VERSION=1.6 -LIBZIP_VERSION=1.0.1 GEOJSONVT_VERSION=2.1.6.3 VARIANT_VERSION=1.0 RAPIDJSON_VERSION=1.0.2 diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index 5b1138a14af..c8d8595ade7 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -10,7 +10,11 @@ namespace mbgl { const std::string AnnotationManager::SourceID = "com.mapbox.annotations"; const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; -AnnotationManager::AnnotationManager() = default; +AnnotationManager::AnnotationManager(float pixelRatio) + : spriteStore(pixelRatio), + spriteAtlas(512, 512, pixelRatio, spriteStore) { +} + AnnotationManager::~AnnotationManager() = default; AnnotationIDs @@ -122,6 +126,7 @@ void AnnotationManager::updateStyle(Style& style) { layer->sourceLayer = PointLayerID; layer->layout.icon.image = std::string("{sprite}"); layer->layout.icon.allowOverlap = true; + layer->spriteAtlas = &spriteAtlas; style.addLayer(std::move(layer)); } @@ -152,4 +157,19 @@ void AnnotationManager::removeTileMonitor(AnnotationTileMonitor& monitor) { monitors.erase(&monitor); } +void AnnotationManager::addIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) { + spriteStore.setSprite(name, sprite); + spriteAtlas.updateDirty(); +} + +void AnnotationManager::removeIcon(const std::string& name) { + spriteStore.removeSprite(name); + spriteAtlas.updateDirty(); +} + +double AnnotationManager::getTopOffsetPixelsForIcon(const std::string& name) { + auto sprite = spriteStore.getSprite(name); + return sprite ? -sprite->height / 2 : 0; } + +} // namespace mbgl diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp index f1b41c9ccce..bc3f73e2cad 100644 --- a/src/mbgl/annotation/annotation_manager.hpp +++ b/src/mbgl/annotation/annotation_manager.hpp @@ -4,6 +4,8 @@ #include <mbgl/annotation/annotation.hpp> #include <mbgl/annotation/point_annotation_impl.hpp> #include <mbgl/annotation/shape_annotation_impl.hpp> +#include <mbgl/sprite/sprite_store.hpp> +#include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/util/geo.hpp> #include <mbgl/util/noncopyable.hpp> @@ -21,7 +23,7 @@ class Style; class AnnotationManager : private util::noncopyable { public: - AnnotationManager(); + AnnotationManager(float pixelRatio); ~AnnotationManager(); AnnotationIDs addPointAnnotations(const std::vector<PointAnnotation>&, const uint8_t maxZoom); @@ -31,6 +33,11 @@ class AnnotationManager : private util::noncopyable { AnnotationIDs getPointAnnotationsInBounds(const LatLngBounds&) const; LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const; + void addIcon(const std::string& name, std::shared_ptr<const SpriteImage>); + void removeIcon(const std::string& name); + double getTopOffsetPixelsForIcon(const std::string& name); + SpriteAtlas& getSpriteAtlas() { return spriteAtlas; } + void updateStyle(Style&); void addTileMonitor(AnnotationTileMonitor&); @@ -48,8 +55,11 @@ class AnnotationManager : private util::noncopyable { ShapeAnnotationImpl::Map shapeAnnotations; std::vector<std::string> obsoleteShapeAnnotationLayers; std::set<AnnotationTileMonitor*> monitors; + + SpriteStore spriteStore; + SpriteAtlas spriteAtlas; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp index f9ab79e4f42..f3897ad3eab 100644 --- a/src/mbgl/annotation/annotation_tile.cpp +++ b/src/mbgl/annotation/annotation_tile.cpp @@ -2,13 +2,14 @@ #include <mbgl/util/constants.hpp> #include <mbgl/map/map_data.hpp> #include <mbgl/storage/file_source.hpp> +#include <utility> namespace mbgl { AnnotationTileFeature::AnnotationTileFeature(FeatureType type_, GeometryCollection geometries_, std::unordered_map<std::string, std::string> properties_) : type(type_), - properties(properties_), + properties(std::move(properties_)), geometries(geometries_) {} mapbox::util::optional<Value> AnnotationTileFeature::getValue(const std::string& key) const { @@ -36,14 +37,14 @@ AnnotationTileMonitor::~AnnotationTileMonitor() { data.getAnnotationManager()->removeTileMonitor(*this); } -std::unique_ptr<FileRequest> AnnotationTileMonitor::monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)> callback_) { +std::unique_ptr<FileRequest> AnnotationTileMonitor::monitorTile(const GeometryTileMonitor::Callback& callback_) { callback = callback_; data.getAnnotationManager()->addTileMonitor(*this); return nullptr; } void AnnotationTileMonitor::update(std::unique_ptr<GeometryTile> tile) { - callback(nullptr, std::move(tile)); + callback(nullptr, std::move(tile), Seconds::zero(), Seconds::zero()); } -} +} // namespace mbgl diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp index 70c9f7265f9..3903e1b79f4 100644 --- a/src/mbgl/annotation/annotation_tile.hpp +++ b/src/mbgl/annotation/annotation_tile.hpp @@ -42,19 +42,20 @@ class MapData; class AnnotationTileMonitor : public GeometryTileMonitor { public: + // TODO: should just take AnnotationManager&, but we need to eliminate util::exclusive<AnnotationManager> from MapData first. AnnotationTileMonitor(const TileID&, MapData&); ~AnnotationTileMonitor(); void update(std::unique_ptr<GeometryTile>); - std::unique_ptr<FileRequest> monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)>) override; + std::unique_ptr<FileRequest> monitorTile(const GeometryTileMonitor::Callback&) override; TileID tileID; private: MapData& data; - std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)> callback; + GeometryTileMonitor::Callback callback; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/annotation/point_annotation_impl.cpp b/src/mbgl/annotation/point_annotation_impl.cpp index d313dab93cd..c84912750fd 100644 --- a/src/mbgl/annotation/point_annotation_impl.cpp +++ b/src/mbgl/annotation/point_annotation_impl.cpp @@ -29,4 +29,4 @@ LatLngBounds PointAnnotationImpl::bounds() const { return LatLngBounds(point.position, point.position); } -} +} // namespace mbgl diff --git a/src/mbgl/annotation/point_annotation_impl.hpp b/src/mbgl/annotation/point_annotation_impl.hpp index 2977caf5773..47c3bffea26 100644 --- a/src/mbgl/annotation/point_annotation_impl.hpp +++ b/src/mbgl/annotation/point_annotation_impl.hpp @@ -50,7 +50,7 @@ class PointAnnotationImpl { const PointAnnotation point; }; -} +} // namespace mbgl // Tell Boost Geometry how to access a std::shared_ptr<mbgl::PointAnnotation> object. namespace boost { diff --git a/src/mbgl/annotation/shape_annotation_impl.cpp b/src/mbgl/annotation/shape_annotation_impl.cpp index 4b60c667aa5..a3f5fd2d6a3 100644 --- a/src/mbgl/annotation/shape_annotation_impl.cpp +++ b/src/mbgl/annotation/shape_annotation_impl.cpp @@ -72,6 +72,7 @@ void ShapeAnnotationImpl::updateStyle(Style& style) { : ProjectedFeatureType::Polygon; layer->id = layerID; + layer->ref = ""; layer->source = AnnotationManager::SourceID; layer->sourceLayer = layer->id; layer->visibility = VisibilityType::Visible; @@ -157,4 +158,4 @@ LatLngBounds ShapeAnnotationImpl::bounds() const { return result; } -} +} // namespace mbgl diff --git a/src/mbgl/annotation/shape_annotation_impl.hpp b/src/mbgl/annotation/shape_annotation_impl.hpp index f312ec75df9..6134e605c2f 100644 --- a/src/mbgl/annotation/shape_annotation_impl.hpp +++ b/src/mbgl/annotation/shape_annotation_impl.hpp @@ -36,6 +36,6 @@ class ShapeAnnotationImpl { std::unique_ptr<mapbox::util::geojsonvt::GeoJSONVT> shapeTiler; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/anchor.hpp b/src/mbgl/geometry/anchor.hpp index 352f260752c..49979cc15ac 100644 --- a/src/mbgl/geometry/anchor.hpp +++ b/src/mbgl/geometry/anchor.hpp @@ -21,6 +21,6 @@ struct Anchor { typedef std::vector<Anchor> Anchors; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/binpack.hpp b/src/mbgl/geometry/binpack.hpp index 504417f452e..706115a2084 100644 --- a/src/mbgl/geometry/binpack.hpp +++ b/src/mbgl/geometry/binpack.hpp @@ -99,6 +99,6 @@ class BinPack : private util::noncopyable { std::list<Rect<T>> free; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/buffer.hpp b/src/mbgl/geometry/buffer.hpp index d54ee18c7ef..c7278a10361 100644 --- a/src/mbgl/geometry/buffer.hpp +++ b/src/mbgl/geometry/buffer.hpp @@ -99,7 +99,7 @@ class Buffer : private util::noncopyable { } if (i * itemSize >= pos) { - throw new std::runtime_error("Can't get element after array bounds"); + throw std::runtime_error("Can't get element after array bounds"); } else { return reinterpret_cast<char *>(array) + (i * itemSize); } @@ -122,6 +122,6 @@ class Buffer : private util::noncopyable { GLuint buffer = 0; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/circle_buffer.hpp b/src/mbgl/geometry/circle_buffer.hpp index cab9122e5b6..30e339bb61e 100644 --- a/src/mbgl/geometry/circle_buffer.hpp +++ b/src/mbgl/geometry/circle_buffer.hpp @@ -22,6 +22,6 @@ class CircleVertexBuffer : public Buffer< void add(vertex_type x, vertex_type y, float ex, float ey); }; -} +} // namespace mbgl #endif // MBGL_GEOMETRY_CIRCLE_BUFFER diff --git a/src/mbgl/geometry/collision_box_buffer.cpp b/src/mbgl/geometry/collision_box_buffer.cpp index da9031c8951..f4f8075e104 100644 --- a/src/mbgl/geometry/collision_box_buffer.cpp +++ b/src/mbgl/geometry/collision_box_buffer.cpp @@ -24,4 +24,4 @@ size_t CollisionBoxVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, f return idx; } -} +} // namespace mbgl diff --git a/src/mbgl/geometry/collision_box_buffer.hpp b/src/mbgl/geometry/collision_box_buffer.hpp index 098eab13c69..44a0f164b8d 100644 --- a/src/mbgl/geometry/collision_box_buffer.hpp +++ b/src/mbgl/geometry/collision_box_buffer.hpp @@ -18,6 +18,6 @@ class CollisionBoxVertexBuffer : public Buffer < }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/debug_font_buffer.hpp b/src/mbgl/geometry/debug_font_buffer.hpp index 802b5dbaac6..904360ee84e 100644 --- a/src/mbgl/geometry/debug_font_buffer.hpp +++ b/src/mbgl/geometry/debug_font_buffer.hpp @@ -12,6 +12,6 @@ class DebugFontBuffer : public Buffer< void addText(const char *text, double left, double baseline, double scale = 1); }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/elements_buffer.hpp b/src/mbgl/geometry/elements_buffer.hpp index 5e642f06dbd..ce716dfaf4a 100644 --- a/src/mbgl/geometry/elements_buffer.hpp +++ b/src/mbgl/geometry/elements_buffer.hpp @@ -44,6 +44,6 @@ class LineElementsBuffer : public Buffer< void add(element_type a, element_type b); }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/fill_buffer.hpp b/src/mbgl/geometry/fill_buffer.hpp index 2cd1637fa1f..531dcf82fcb 100644 --- a/src/mbgl/geometry/fill_buffer.hpp +++ b/src/mbgl/geometry/fill_buffer.hpp @@ -16,6 +16,6 @@ class FillVertexBuffer : public Buffer< void add(vertex_type x, vertex_type y); }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/glyph_atlas.hpp b/src/mbgl/geometry/glyph_atlas.hpp index 12053018353..5b4ae1fc6b5 100644 --- a/src/mbgl/geometry/glyph_atlas.hpp +++ b/src/mbgl/geometry/glyph_atlas.hpp @@ -56,6 +56,6 @@ class GlyphAtlas : public util::noncopyable { GLuint texture = 0; }; -}; +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/icon_buffer.cpp b/src/mbgl/geometry/icon_buffer.cpp index 831ffc72ac7..4e6a1c52e74 100644 --- a/src/mbgl/geometry/icon_buffer.cpp +++ b/src/mbgl/geometry/icon_buffer.cpp @@ -29,4 +29,4 @@ size_t IconVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, int16_t t return idx; } -} +} // namespace mbgl diff --git a/src/mbgl/geometry/icon_buffer.hpp b/src/mbgl/geometry/icon_buffer.hpp index 95469929cf9..d3c3999ee9d 100644 --- a/src/mbgl/geometry/icon_buffer.hpp +++ b/src/mbgl/geometry/icon_buffer.hpp @@ -15,6 +15,6 @@ namespace mbgl { }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/line_atlas.hpp b/src/mbgl/geometry/line_atlas.hpp index a8663cb8201..e76a91b61d9 100644 --- a/src/mbgl/geometry/line_atlas.hpp +++ b/src/mbgl/geometry/line_atlas.hpp @@ -41,6 +41,6 @@ class LineAtlas { std::map<size_t, LinePatternPos> positions; }; -}; +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/line_buffer.cpp b/src/mbgl/geometry/line_buffer.cpp index 77a134b4688..f775a4589f0 100644 --- a/src/mbgl/geometry/line_buffer.cpp +++ b/src/mbgl/geometry/line_buffer.cpp @@ -5,7 +5,7 @@ using namespace mbgl; -GLsizei LineVertexBuffer::add(vertex_type x, vertex_type y, float ex, float ey, int8_t tx, int8_t ty, int32_t linesofar) { +GLsizei LineVertexBuffer::add(vertex_type x, vertex_type y, float ex, float ey, bool tx, bool ty, int8_t dir, int32_t linesofar) { GLsizei idx = index(); void *data = addElement(); @@ -16,8 +16,13 @@ GLsizei LineVertexBuffer::add(vertex_type x, vertex_type y, float ex, float ey, int8_t *extrude = static_cast<int8_t *>(data); extrude[4] = ::round(extrudeScale * ex); extrude[5] = ::round(extrudeScale * ey); - extrude[6] = static_cast<int8_t>(linesofar / 128); - extrude[7] = static_cast<int8_t>(linesofar % 128); + + // Encode the -1/0/1 direction value into .zw coordinates of a_data, which is normally covered + // by linesofar, so we need to merge them. + // The z component's first bit, as well as the sign bit is reserved for the direction, + // so we need to shift the linesofar. + extrude[6] = ((dir < 0) ? -1 : 1) * ((dir ? 1 : 0) | static_cast<int8_t>((linesofar << 1) & 0x7F)); + extrude[7] = (linesofar >> 6) & 0x7F; return idx; } diff --git a/src/mbgl/geometry/line_buffer.hpp b/src/mbgl/geometry/line_buffer.hpp index 7174fa2910b..aa2dedbceb1 100644 --- a/src/mbgl/geometry/line_buffer.hpp +++ b/src/mbgl/geometry/line_buffer.hpp @@ -29,11 +29,12 @@ class LineVertexBuffer : public Buffer< * @param {number} ey extrude normal * @param {number} tx texture normal * @param {number} ty texture normal + * @param {number} dir direction of the line cap (-1/0/1) */ - GLsizei add(vertex_type x, vertex_type y, float ex, float ey, int8_t tx, int8_t ty, int32_t linesofar = 0); + GLsizei add(vertex_type x, vertex_type y, float ex, float ey, bool tx, bool ty, int8_t dir, int32_t linesofar = 0); }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/static_vertex_buffer.cpp b/src/mbgl/geometry/static_vertex_buffer.cpp index c1e8caab9e7..e8dad0ba9b5 100644 --- a/src/mbgl/geometry/static_vertex_buffer.cpp +++ b/src/mbgl/geometry/static_vertex_buffer.cpp @@ -11,4 +11,4 @@ StaticVertexBuffer::StaticVertexBuffer(std::initializer_list<std::pair<int16_t, } } -} +} // namespace mbgl diff --git a/src/mbgl/geometry/static_vertex_buffer.hpp b/src/mbgl/geometry/static_vertex_buffer.hpp index ce932269f0f..eaf05b44d3a 100644 --- a/src/mbgl/geometry/static_vertex_buffer.hpp +++ b/src/mbgl/geometry/static_vertex_buffer.hpp @@ -21,6 +21,6 @@ class StaticVertexBuffer : public Buffer< StaticVertexBuffer(std::initializer_list<std::pair<int16_t, int16_t>> init); }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/text_buffer.cpp b/src/mbgl/geometry/text_buffer.cpp index 8f312c9faf4..530048a6ddf 100644 --- a/src/mbgl/geometry/text_buffer.cpp +++ b/src/mbgl/geometry/text_buffer.cpp @@ -29,4 +29,4 @@ size_t TextVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, uint16_t return idx; } -} +} // namespace mbgl diff --git a/src/mbgl/geometry/text_buffer.hpp b/src/mbgl/geometry/text_buffer.hpp index 895d4723769..5a20d463ba1 100644 --- a/src/mbgl/geometry/text_buffer.hpp +++ b/src/mbgl/geometry/text_buffer.hpp @@ -18,6 +18,6 @@ class TextVertexBuffer : public Buffer < }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/geometry/vao.cpp b/src/mbgl/geometry/vao.cpp index 1330a057f97..d3ad16e64f2 100644 --- a/src/mbgl/geometry/vao.cpp +++ b/src/mbgl/geometry/vao.cpp @@ -92,4 +92,4 @@ void VertexArrayObject::storeBinding(Shader &shader, GLuint vertexBuffer, GLuint bound_elements_buffer = elementsBuffer; } -} +} // namespace mbgl diff --git a/src/mbgl/geometry/vao.hpp b/src/mbgl/geometry/vao.hpp index bd845b1e330..2dcc02c76bd 100644 --- a/src/mbgl/geometry/vao.hpp +++ b/src/mbgl/geometry/vao.hpp @@ -66,6 +66,6 @@ class VertexArrayObject : public util::noncopyable { GLbyte *bound_offset = 0; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/gl/debugging.cpp b/src/mbgl/gl/debugging.cpp index 0061127048f..fb9e0377149 100644 --- a/src/mbgl/gl/debugging.cpp +++ b/src/mbgl/gl/debugging.cpp @@ -185,6 +185,6 @@ group::~group() { } } -} -} -} +} // namespace debugging +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/debugging.hpp b/src/mbgl/gl/debugging.hpp index 046f0169d9f..5321858624c 100644 --- a/src/mbgl/gl/debugging.hpp +++ b/src/mbgl/gl/debugging.hpp @@ -22,8 +22,8 @@ struct group { ~group(); }; -} -} -} +} // namespace debugging +} // namespace gl +} // namespace mbgl #endif diff --git a/src/mbgl/layer/background_layer.cpp b/src/mbgl/layer/background_layer.cpp index 51000e53ba6..f3ea723b334 100644 --- a/src/mbgl/layer/background_layer.cpp +++ b/src/mbgl/layer/background_layer.cpp @@ -38,4 +38,4 @@ std::unique_ptr<Bucket> BackgroundLayer::createBucket(StyleBucketParameters&) co return nullptr; } -} +} // namespace mbgl diff --git a/src/mbgl/layer/background_layer.hpp b/src/mbgl/layer/background_layer.hpp index a65b9e01835..ee8d3fc88a4 100644 --- a/src/mbgl/layer/background_layer.hpp +++ b/src/mbgl/layer/background_layer.hpp @@ -28,6 +28,6 @@ class BackgroundLayer : public StyleLayer { BackgroundPaintProperties paint; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/layer/circle_layer.cpp b/src/mbgl/layer/circle_layer.cpp index e33476a4a81..7559a032844 100644 --- a/src/mbgl/layer/circle_layer.cpp +++ b/src/mbgl/layer/circle_layer.cpp @@ -54,4 +54,4 @@ std::unique_ptr<Bucket> CircleLayer::createBucket(StyleBucketParameters& paramet return std::move(bucket); } -} +} // namespace mbgl diff --git a/src/mbgl/layer/circle_layer.hpp b/src/mbgl/layer/circle_layer.hpp index eac9a915cc1..3e4581ef62c 100644 --- a/src/mbgl/layer/circle_layer.hpp +++ b/src/mbgl/layer/circle_layer.hpp @@ -35,6 +35,6 @@ class CircleLayer : public StyleLayer { CirclePaintProperties paint; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/layer/fill_layer.cpp b/src/mbgl/layer/fill_layer.cpp index 52a6b8fe55c..c2b9227558c 100644 --- a/src/mbgl/layer/fill_layer.cpp +++ b/src/mbgl/layer/fill_layer.cpp @@ -67,4 +67,4 @@ std::unique_ptr<Bucket> FillLayer::createBucket(StyleBucketParameters& parameter return std::move(bucket); } -} +} // namespace mbgl diff --git a/src/mbgl/layer/fill_layer.hpp b/src/mbgl/layer/fill_layer.hpp index c0ed228d486..1132fbf4e81 100644 --- a/src/mbgl/layer/fill_layer.hpp +++ b/src/mbgl/layer/fill_layer.hpp @@ -32,6 +32,6 @@ class FillLayer : public StyleLayer { FillPaintProperties paint; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/layer/line_layer.cpp b/src/mbgl/layer/line_layer.cpp index f4453f819df..1e25ed30cb3 100644 --- a/src/mbgl/layer/line_layer.cpp +++ b/src/mbgl/layer/line_layer.cpp @@ -27,6 +27,7 @@ void LineLayer::parsePaints(const JSVal& layer) { paint.translateAnchor.parse("line-translate-anchor", layer); paint.width.parse("line-width", layer); paint.gapWidth.parse("line-gap-width", layer); + paint.offset.parse("line-offset", layer); paint.blur.parse("line-blur", layer); paint.dasharray.parse("line-dasharray", layer); paint.pattern.parse("line-pattern", layer); @@ -39,6 +40,7 @@ void LineLayer::cascade(const StyleCascadeParameters& parameters) { paint.translateAnchor.cascade(parameters); paint.width.cascade(parameters); paint.gapWidth.cascade(parameters); + paint.offset.cascade(parameters); paint.blur.cascade(parameters); paint.dasharray.cascade(parameters); paint.pattern.cascade(parameters); @@ -59,6 +61,7 @@ bool LineLayer::recalculate(const StyleCalculationParameters& parameters) { hasTransitions |= paint.translateAnchor.calculate(parameters); hasTransitions |= paint.width.calculate(parameters); hasTransitions |= paint.gapWidth.calculate(parameters); + hasTransitions |= paint.offset.calculate(parameters); hasTransitions |= paint.blur.calculate(parameters); hasTransitions |= paint.dasharray.calculate(parameters); hasTransitions |= paint.pattern.calculate(parameters); @@ -86,4 +89,4 @@ std::unique_ptr<Bucket> LineLayer::createBucket(StyleBucketParameters& parameter return std::move(bucket); } -} +} // namespace mbgl diff --git a/src/mbgl/layer/line_layer.hpp b/src/mbgl/layer/line_layer.hpp index 639ba34f57e..d1c654619a2 100644 --- a/src/mbgl/layer/line_layer.hpp +++ b/src/mbgl/layer/line_layer.hpp @@ -24,6 +24,7 @@ class LinePaintProperties { PaintProperty<float> width = 1; PaintProperty<float> gapWidth = 0; PaintProperty<float> blur = 0; + PaintProperty<float> offset = 0; PaintProperty<std::vector<float>, Faded<std::vector<float>>> dasharray = { {} }; PaintProperty<std::string, Faded<std::string>> pattern = { "" }; @@ -51,6 +52,6 @@ class LineLayer : public StyleLayer { LinePaintProperties paint; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/layer/raster_layer.cpp b/src/mbgl/layer/raster_layer.cpp index 5a7e6864416..87199f24f7f 100644 --- a/src/mbgl/layer/raster_layer.cpp +++ b/src/mbgl/layer/raster_layer.cpp @@ -50,4 +50,4 @@ std::unique_ptr<Bucket> RasterLayer::createBucket(StyleBucketParameters&) const return nullptr; } -} +} // namespace mbgl diff --git a/src/mbgl/layer/raster_layer.hpp b/src/mbgl/layer/raster_layer.hpp index 800236016da..43ffa488698 100644 --- a/src/mbgl/layer/raster_layer.hpp +++ b/src/mbgl/layer/raster_layer.hpp @@ -32,6 +32,6 @@ class RasterLayer : public StyleLayer { RasterPaintProperties paint; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/layer/symbol_layer.cpp b/src/mbgl/layer/symbol_layer.cpp index 8e43d3454e2..c7b4824deb0 100644 --- a/src/mbgl/layer/symbol_layer.cpp +++ b/src/mbgl/layer/symbol_layer.cpp @@ -10,6 +10,7 @@ std::unique_ptr<StyleLayer> SymbolLayer::clone() const { result->copy(*this); result->layout = layout; result->paint = paint; + result->spriteAtlas = spriteAtlas; return std::move(result); } @@ -178,7 +179,7 @@ std::unique_ptr<Bucket> SymbolLayer::createBucket(StyleBucketParameters& paramet // needed by this tile. if (!parameters.partialParse) { bucket->addFeatures(parameters.tileUID, - parameters.spriteAtlas, + *spriteAtlas, parameters.glyphAtlas, parameters.glyphStore, parameters.collisionTile); @@ -187,4 +188,4 @@ std::unique_ptr<Bucket> SymbolLayer::createBucket(StyleBucketParameters& paramet return std::move(bucket); } -} +} // namespace mbgl diff --git a/src/mbgl/layer/symbol_layer.hpp b/src/mbgl/layer/symbol_layer.hpp index 9e4555350a2..4b37d120550 100644 --- a/src/mbgl/layer/symbol_layer.hpp +++ b/src/mbgl/layer/symbol_layer.hpp @@ -7,6 +7,8 @@ namespace mbgl { +class SpriteAtlas; + class SymbolLayoutProperties { public: LayoutProperty<PlacementType> placement = PlacementType::Point; @@ -94,8 +96,10 @@ class SymbolLayer : public StyleLayer { SymbolLayoutProperties layout; SymbolPaintProperties paint; + + SpriteAtlas* spriteAtlas; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/geometry_tile.cpp b/src/mbgl/map/geometry_tile.cpp index 785845b4dbf..1bb132f3162 100644 --- a/src/mbgl/map/geometry_tile.cpp +++ b/src/mbgl/map/geometry_tile.cpp @@ -14,4 +14,4 @@ mapbox::util::optional<Value> GeometryTileFeatureExtractor::getValue(const std:: template bool evaluate(const FilterExpression&, const GeometryTileFeatureExtractor&); -} +} // namespace mbgl diff --git a/src/mbgl/map/geometry_tile.hpp b/src/mbgl/map/geometry_tile.hpp index d6717ddc471..4ae08cc9d83 100644 --- a/src/mbgl/map/geometry_tile.hpp +++ b/src/mbgl/map/geometry_tile.hpp @@ -5,6 +5,7 @@ #include <mapbox/optional.hpp> #include <mbgl/style/value.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/ptr.hpp> #include <mbgl/util/vec.hpp> #include <mbgl/util/noncopyable.hpp> @@ -52,6 +53,10 @@ class GeometryTileMonitor : private util::noncopyable { public: virtual ~GeometryTileMonitor() = default; + using Callback = std::function<void (std::exception_ptr, + std::unique_ptr<GeometryTile>, + Seconds modified, + Seconds expires)>; /* * Monitor the tile held by this object for changes. When the tile is loaded for the first time, * or updates, the callback is executed. If an error occurs, the first parameter will be set. @@ -60,7 +65,7 @@ class GeometryTileMonitor : private util::noncopyable { * * To cease monitoring, release the returned Request. */ - virtual std::unique_ptr<FileRequest> monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)>) = 0; + virtual std::unique_ptr<FileRequest> monitorTile(const Callback&) = 0; }; class GeometryTileFeatureExtractor { @@ -74,6 +79,6 @@ class GeometryTileFeatureExtractor { const GeometryTileFeature& feature; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 713222fe54b..b5f9f50ce51 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -17,8 +17,10 @@ namespace mbgl { Map::Map(View& view_, FileSource& fileSource, MapMode mapMode, GLContextMode contextMode, ConstrainMode constrainMode) : view(view_), transform(std::make_unique<Transform>(view, constrainMode)), - data(std::make_unique<MapData>(mapMode, contextMode, view.getPixelRatio())), - context(std::make_unique<util::Thread<MapContext>>(util::ThreadContext{"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, view, fileSource, *data)) + context(std::make_unique<util::Thread<MapContext>>( + util::ThreadContext{"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, + view, fileSource, mapMode, contextMode, view.getPixelRatio())), + data(&context->invokeSync<MapData&>(&MapContext::getData)) { view.initialize(this); update(Update::Dimensions); @@ -365,8 +367,16 @@ LatLng Map::latLngForPixel(const PrecisionPoint& pixel) const { #pragma mark - Annotations -double Map::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) { - return context->invokeSync<double>(&MapContext::getTopOffsetPixelsForAnnotationSymbol, symbol); +void Map::addAnnotationIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) { + context->invoke(&MapContext::addAnnotationIcon, name, sprite); +} + +void Map::removeAnnotationIcon(const std::string& name) { + removeAnnotationIcon(name); +} + +double Map::getTopOffsetPixelsForAnnotationIcon(const std::string& symbol) { + return context->invokeSync<double>(&MapContext::getTopOffsetPixelsForAnnotationIcon, symbol); } AnnotationID Map::addPointAnnotation(const PointAnnotation& annotation) { @@ -406,48 +416,22 @@ LatLngBounds Map::getBoundsForAnnotations(const AnnotationIDs& annotations) { return data->getAnnotationManager()->getBoundsForAnnotations(annotations); } - -#pragma mark - Sprites - -void Map::setSprite(const std::string& name, std::shared_ptr<const SpriteImage> sprite) { - context->invoke(&MapContext::setSprite, name, sprite); -} - -void Map::removeSprite(const std::string& name) { - setSprite(name, nullptr); -} - - #pragma mark - Toggles -void Map::setDebug(bool value) { - data->setDebug(value); +void Map::setDebug(MapDebugOptions mode) { + data->setDebug(mode); update(Update::Repaint); } -void Map::toggleDebug() { - data->toggleDebug(); +void Map::cycleDebugOptions() { + data->cycleDebugOptions(); update(Update::Repaint); } -bool Map::getDebug() const { +MapDebugOptions Map::getDebug() const { return data->getDebug(); } -void Map::setCollisionDebug(bool value) { - data->setCollisionDebug(value); - update(Update::Repaint); -} - -void Map::toggleCollisionDebug() { - data->toggleCollisionDebug(); - update(Update::Repaint); -} - -bool Map::getCollisionDebug() const { - return data->getCollisionDebug(); -} - bool Map::isFullyLoaded() const { return context->invokeSync<bool>(&MapContext::isLoaded); } @@ -516,4 +500,4 @@ void Map::dumpDebugLogs() const { context->invokeSync(&MapContext::dumpDebugLogs); } -} +} // namespace mbgl diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index f00b3eaf555..bb288b8fe92 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -16,7 +16,6 @@ #include <mbgl/sprite/sprite_store.hpp> #include <mbgl/util/gl_object_store.hpp> -#include <mbgl/util/uv_detail.hpp> #include <mbgl/util/worker.hpp> #include <mbgl/util/texture_pool.hpp> #include <mbgl/util/exception.hpp> @@ -26,19 +25,20 @@ namespace mbgl { -MapContext::MapContext(View& view_, FileSource& fileSource, MapData& data_) +MapContext::MapContext(View& view_, FileSource& fileSource, MapMode mode_, GLContextMode contextMode_, const float pixelRatio_) : view(view_), - data(data_), - asyncUpdate(std::make_unique<uv::async>(util::RunLoop::getLoop(), [this] { update(); })), - asyncInvalidate(std::make_unique<uv::async>(util::RunLoop::getLoop(), [&view_] { view_.invalidate(); })), + dataPtr(std::make_unique<MapData>(mode_, contextMode_, pixelRatio_)), + data(*dataPtr), + asyncUpdate([this] { update(); }), + asyncInvalidate([&view_] { view_.invalidate(); }), texturePool(std::make_unique<TexturePool>()) { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); util::ThreadContext::setFileSource(&fileSource); util::ThreadContext::setGLObjectStore(&glObjectStore); - asyncUpdate->unref(); - asyncInvalidate->unref(); + asyncUpdate.unref(); + asyncInvalidate.unref(); view.activate(); } @@ -58,6 +58,7 @@ void MapContext::cleanup() { style.reset(); painter.reset(); texturePool.reset(); + dataPtr.reset(); glObjectStore.performCleanup(); @@ -76,14 +77,14 @@ void MapContext::pause() { view.activate(); - asyncInvalidate->send(); + asyncInvalidate.send(); } void MapContext::triggerUpdate(const TransformState& state, const Update flags) { transformState = state; updateFlags |= flags; - asyncUpdate->send(); + asyncUpdate.send(); } void MapContext::setStyleURL(const std::string& url) { @@ -152,7 +153,7 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base) data.loading = true; updateFlags |= Update::DefaultTransition | Update::Classes | Update::Zoom | Update::Annotations; - asyncUpdate->send(); + asyncUpdate.send(); } void MapContext::update() { @@ -184,7 +185,7 @@ void MapContext::update() { style->update(transformState, *texturePool); if (data.mode == MapMode::Continuous) { - asyncInvalidate->send(); + asyncInvalidate.send(); } else if (callback && style->isLoaded()) { renderSync(transformState, frameData); } @@ -223,7 +224,7 @@ void MapContext::renderStill(const TransformState& state, const FrameData& frame frameData = frame; updateFlags |= Update::RenderStill; - asyncUpdate->send(); + asyncUpdate.send(); } bool MapContext::renderSync(const TransformState& state, const FrameData& frame) { @@ -242,10 +243,10 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame) glObjectStore.performCleanup(); if (!painter) painter = std::make_unique<Painter>(data, transformState); - painter->render(*style, frame); + painter->render(*style, frame, data.getAnnotationManager()->getSpriteAtlas()); if (data.mode == MapMode::Still) { - callback(nullptr, std::move(view.readStillImage())); + callback(nullptr, view.readStillImage()); callback = nullptr; } @@ -253,10 +254,10 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame) if (style->hasTransitions()) { updateFlags |= Update::Classes; - asyncUpdate->send(); + asyncUpdate.send(); } else if (painter->needsAnimation()) { updateFlags |= Update::Repaint; - asyncUpdate->send(); + asyncUpdate.send(); } return isLoaded(); @@ -266,14 +267,19 @@ bool MapContext::isLoaded() const { return style->isLoaded(); } -double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) { +void MapContext::addAnnotationIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); - auto sprite = style->spriteStore->getSprite(symbol); - if (sprite) { - return -sprite->height / 2; - } else { - return 0; - } + data.getAnnotationManager()->addIcon(name, sprite); +} + +void MapContext::removeAnnotationIcon(const std::string& name) { + assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); + data.getAnnotationManager()->removeIcon(name); +} + +double MapContext::getTopOffsetPixelsForAnnotationIcon(const std::string& name) { + assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); + return data.getAnnotationManager()->getTopOffsetPixelsForIcon(name); } void MapContext::setSourceTileCacheSize(size_t size) { @@ -281,38 +287,22 @@ void MapContext::setSourceTileCacheSize(size_t size) { if (size != sourceCacheSize) { sourceCacheSize = size; if (!style) return; - for (const auto &source : style->sources) { - source->setCacheSize(sourceCacheSize); - } - asyncInvalidate->send(); + style->setSourceTileCacheSize(size); + asyncInvalidate.send(); } } void MapContext::onLowMemory() { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); if (!style) return; - for (const auto &source : style->sources) { - source->onLowMemory(); - } - asyncInvalidate->send(); + style->onLowMemory(); + asyncInvalidate.send(); } - -void MapContext::setSprite(const std::string& name, std::shared_ptr<const SpriteImage> sprite) { - if (!style) { - Log::Info(Event::Sprite, "Ignoring sprite without stylesheet"); - return; - } - - style->spriteStore->setSprite(name, sprite); - - style->spriteAtlas->updateDirty(); -} - + void MapContext::onTileDataChanged() { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); - updateFlags |= Update::Repaint; - asyncUpdate->send(); + asyncUpdate.send(); } void MapContext::onResourceLoadingFailed(std::exception_ptr error) { @@ -335,4 +325,4 @@ void MapContext::dumpDebugLogs() const { Log::Info(Event::General, "--------------------------------------------------------------------------------"); } -} +} // namespace mbgl diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index f4df3aae255..2e54c4ad73c 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -5,16 +5,14 @@ #include <mbgl/map/update.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/map/map.hpp> +#include <mbgl/map/map_data.hpp> #include <mbgl/style/style.hpp> +#include <mbgl/util/async_task.hpp> #include <mbgl/util/gl_object_store.hpp> #include <mbgl/util/ptr.hpp> #include <vector> -namespace uv { -class async; -} - namespace mbgl { class View; @@ -30,9 +28,11 @@ struct FrameData { class MapContext : public Style::Observer { public: - MapContext(View&, FileSource&, MapData&); + MapContext(View&, FileSource&, MapMode, GLContextMode, const float pixelRatio); ~MapContext(); + MapData& getData() { return data; } + void pause(); void triggerUpdate(const TransformState&, Update = Update::Nothing); @@ -48,7 +48,9 @@ class MapContext : public Style::Observer { bool isLoaded() const; - double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol); + void addAnnotationIcon(const std::string&, std::shared_ptr<const SpriteImage>); + void removeAnnotationIcon(const std::string&); + double getTopOffsetPixelsForAnnotationIcon(const std::string&); void updateAnnotations(); void setSourceTileCacheSize(size_t size); @@ -56,8 +58,6 @@ class MapContext : public Style::Observer { void cleanup(); - void setSprite(const std::string&, std::shared_ptr<const SpriteImage>); - // Style::Observer implementation. void onTileDataChanged() override; void onResourceLoadingFailed(std::exception_ptr error) override; @@ -72,13 +72,14 @@ class MapContext : public Style::Observer { void loadStyleJSON(const std::string& json, const std::string& base); View& view; + std::unique_ptr<MapData> dataPtr; MapData& data; util::GLObjectStore glObjectStore; Update updateFlags = Update::Nothing; - std::unique_ptr<uv::async> asyncUpdate; - std::unique_ptr<uv::async> asyncInvalidate; + util::AsyncTask asyncUpdate; + util::AsyncTask asyncInvalidate; std::unique_ptr<TexturePool> texturePool; std::unique_ptr<Painter> painter; @@ -95,6 +96,6 @@ class MapContext : public Style::Observer { FrameData frameData; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/map_data.cpp b/src/mbgl/map/map_data.cpp index 993edb38e80..3a0de22d0de 100644 --- a/src/mbgl/map/map_data.cpp +++ b/src/mbgl/map/map_data.cpp @@ -41,4 +41,4 @@ std::vector<std::string> MapData::getClasses() const { return classes; } -} +} // namespace mbgl diff --git a/src/mbgl/map/map_data.hpp b/src/mbgl/map/map_data.hpp index c1898fc37f7..97d16b1c216 100644 --- a/src/mbgl/map/map_data.hpp +++ b/src/mbgl/map/map_data.hpp @@ -24,6 +24,7 @@ class MapData { : mode(mode_) , contextMode(contextMode_) , pixelRatio(pixelRatio_) + , annotationManager(pixelRatio) , animationTime(Duration::zero()) , defaultFadeDuration(mode_ == MapMode::Continuous ? std::chrono::milliseconds(300) : Duration::zero()) , defaultTransitionDuration(Duration::zero()) @@ -49,24 +50,25 @@ class MapData { std::vector<std::string> getClasses() const; - inline bool getDebug() const { - return debug; - } - inline bool toggleDebug() { - return debug ^= 1u; - } - inline void setDebug(bool value) { - debug = value; + inline MapDebugOptions getDebug() const { + return debugOptions; } - inline bool getCollisionDebug() const { - return collisionDebug; + inline void cycleDebugOptions() { + if (debugOptions & MapDebugOptions::Collision) + debugOptions = MapDebugOptions::NoDebug; + else if (debugOptions & MapDebugOptions::Timestamps) + debugOptions = debugOptions | MapDebugOptions::Collision; + else if (debugOptions & MapDebugOptions::ParseStatus) + debugOptions = debugOptions | MapDebugOptions::Timestamps; + else if (debugOptions & MapDebugOptions::TileBorders) + debugOptions = debugOptions | MapDebugOptions::ParseStatus; + else + debugOptions = MapDebugOptions::TileBorders; } - inline bool toggleCollisionDebug() { - return collisionDebug ^= 1u; - } - inline void setCollisionDebug(bool value) { - collisionDebug = value; + + inline void setDebug(MapDebugOptions debugOptions_) { + debugOptions = debugOptions_; } inline TimePoint getAnimationTime() const { @@ -136,8 +138,7 @@ class MapData { mutable std::mutex mtx; std::vector<std::string> classes; - std::atomic<uint8_t> debug { false }; - std::atomic<uint8_t> collisionDebug { false }; + std::atomic<MapDebugOptions> debugOptions { MapDebugOptions::NoDebug }; std::atomic<Duration> animationTime; std::atomic<Duration> defaultFadeDuration; std::atomic<Duration> defaultTransitionDuration; @@ -151,6 +152,6 @@ class MapData { bool loading = false; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp index 6d0813ee32d..7ef13301e8b 100644 --- a/src/mbgl/map/raster_tile_data.cpp +++ b/src/mbgl/map/raster_tile_data.cpp @@ -25,7 +25,7 @@ RasterTileData::~RasterTileData() { } void RasterTileData::request(float pixelRatio, - const std::function<void()>& callback) { + const RasterTileData::Callback& callback) { std::string url = source.tileURL(id, pixelRatio); state = State::loading; @@ -55,6 +55,9 @@ void RasterTileData::request(float pixelRatio, state = State::loaded; } + modified = res.modified; + expires = res.expires; + workRequest = worker.parseRasterTile(std::make_unique<RasterBucket>(texturePool), res.data, [this, callback] (RasterTileParseResult result) { workRequest.reset(); if (state != State::loaded) { diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp index f19747c4414..54330b73a80 100644 --- a/src/mbgl/map/raster_tile_data.hpp +++ b/src/mbgl/map/raster_tile_data.hpp @@ -17,8 +17,10 @@ class RasterTileData : public TileData { RasterTileData(const TileID&, TexturePool&, const SourceInfo&, Worker&); ~RasterTileData(); + using Callback = std::function<void()>; + void request(float pixelRatio, - const std::function<void()>& callback); + const Callback& callback); void cancel() override; @@ -35,6 +37,6 @@ class RasterTileData : public TileData { std::unique_ptr<WorkRequest> workRequest; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 3237c544da3..16e2630a946 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -15,6 +15,7 @@ #include <mbgl/util/mapbox.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/style/style_layer.hpp> +#include <mbgl/style/style_update_parameters.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/token.hpp> @@ -231,26 +232,22 @@ TileData::State Source::hasTile(const TileID& id) { bool Source::handlePartialTile(const TileID& id, Worker&) { const TileID normalized_id = id.normalized(); - auto it = tile_data.find(normalized_id); - if (it == tile_data.end()) { + auto it = tileDataMap.find(normalized_id); + if (it == tileDataMap.end()) { return true; } - auto data = it->second.lock(); - if (!data) { + auto tileData = it->second.lock(); + if (!tileData) { return true; } - return data->parsePending([this]() { + return tileData->parsePending([this]() { emitTileLoaded(false); }); } -TileData::State Source::addTile(MapData& data, - const TransformState& transformState, - Style& style, - TexturePool& texturePool, - const TileID& id) { +TileData::State Source::addTile(const TileID& id, const StyleUpdateParameters& parameters) { const TileData::State state = hasTile(id); if (state != TileData::State::invalid) { @@ -265,8 +262,8 @@ TileData::State Source::addTile(MapData& data, // Try to find the associated TileData object. const TileID normalized_id = id.normalized(); - auto it = tile_data.find(normalized_id); - if (it != tile_data.end()) { + auto it = tileDataMap.find(normalized_id); + if (it != tileDataMap.end()) { // Create a shared_ptr handle. Note that this might be empty! new_tile.data = it->second.lock(); } @@ -281,20 +278,24 @@ TileData::State Source::addTile(MapData& data, } if (!new_tile.data) { - auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id, transformState, data.getCollisionDebug()); + auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id, parameters.transformState, parameters.debugOptions & MapDebugOptions::Collision); // If we don't find working tile data, we're just going to load it. if (info.type == SourceType::Raster) { - auto tileData = std::make_shared<RasterTileData>(normalized_id, texturePool, info, style.workers); - tileData->request(data.pixelRatio, callback); + auto tileData = std::make_shared<RasterTileData>(normalized_id, + parameters.texturePool, + info, + parameters.worker); + + tileData->request(parameters.pixelRatio, callback); new_tile.data = tileData; } else { std::unique_ptr<GeometryTileMonitor> monitor; if (info.type == SourceType::Vector) { - monitor = std::make_unique<VectorTileMonitor>(info, normalized_id, data.pixelRatio); + monitor = std::make_unique<VectorTileMonitor>(info, normalized_id, parameters.pixelRatio); } else if (info.type == SourceType::Annotations) { - monitor = std::make_unique<AnnotationTileMonitor>(normalized_id, data); + monitor = std::make_unique<AnnotationTileMonitor>(normalized_id, parameters.data); } else { throw std::runtime_error("source type not implemented"); } @@ -302,11 +303,11 @@ TileData::State Source::addTile(MapData& data, new_tile.data = std::make_shared<VectorTileData>(normalized_id, std::move(monitor), info.source_id, - style, + parameters.style, callback); } - tile_data.emplace(new_tile.data->id, new_tile.data); + tileDataMap.emplace(new_tile.data->id, new_tile.data); } return new_tile.data->getState(); @@ -404,24 +405,20 @@ void Source::findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::fo } } -bool Source::update(MapData& data, - const TransformState& transformState, - Style& style, - TexturePool& texturePool, - bool shouldReparsePartialTiles) { +bool Source::update(const StyleUpdateParameters& parameters) { bool allTilesUpdated = true; - if (!loaded || data.getAnimationTime() <= updated) { + if (!loaded || parameters.animationTime <= updated) { return allTilesUpdated; } - double zoom = getZoom(transformState); + double zoom = getZoom(parameters.transformState); if (info.type == SourceType::Raster || info.type == SourceType::Video) { zoom = ::round(zoom); } else { zoom = std::floor(zoom); } - std::forward_list<TileID> required = coveringTiles(transformState); + std::forward_list<TileID> required = coveringTiles(parameters.transformState); // Determine the overzooming/underzooming amounts. int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info.min_zoom, info.max_zoom); @@ -438,14 +435,14 @@ bool Source::update(MapData& data, switch (state) { case TileData::State::partial: - if (shouldReparsePartialTiles) { - if (!handlePartialTile(id, style.workers)) { + if (parameters.shouldReparsePartialTiles) { + if (!handlePartialTile(id, parameters.worker)) { allTilesUpdated = false; } } break; case TileData::State::invalid: - state = addTile(data, transformState, style, texturePool, id); + state = addTile(id, parameters); break; default: break; @@ -468,9 +465,9 @@ bool Source::update(MapData& data, } if (info.type != SourceType::Raster && cache.getSize() == 0) { - size_t conservativeCacheSize = ((float)transformState.getWidth() / util::tileSize) * - ((float)transformState.getHeight() / util::tileSize) * - (transformState.getMaxZoom() - transformState.getMinZoom() + 1) * + size_t conservativeCacheSize = ((float)parameters.transformState.getWidth() / util::tileSize) * + ((float)parameters.transformState.getHeight() / util::tileSize) * + (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) * 0.5; cache.setSize(conservativeCacheSize); } @@ -496,7 +493,7 @@ bool Source::update(MapData& data, }); // Remove all the expired pointers from the set. - util::erase_if(tile_data, [&retain_data, &tileCache](std::pair<const TileID, std::weak_ptr<TileData>> &pair) { + util::erase_if(tileDataMap, [&retain_data, &tileCache](std::pair<const TileID, std::weak_ptr<TileData>> &pair) { const util::ptr<TileData> tile = pair.second.lock(); if (!tile) { return true; @@ -517,10 +514,10 @@ bool Source::update(MapData& data, for (auto& tilePtr : tilePtrs) { tilePtr->data->redoPlacement( - { transformState.getAngle(), transformState.getPitch(), data.getCollisionDebug() }); + { parameters.transformState.getAngle(), parameters.transformState.getPitch(), parameters.debugOptions & MapDebugOptions::Collision }); } - updated = data.getAnimationTime(); + updated = parameters.animationTime; return allTilesUpdated; } @@ -545,22 +542,22 @@ void Source::setObserver(Observer* observer) { } void Source::tileLoadingCompleteCallback(const TileID& normalized_id, const TransformState& transformState, bool collisionDebug) { - auto it = tile_data.find(normalized_id); - if (it == tile_data.end()) { + auto it = tileDataMap.find(normalized_id); + if (it == tileDataMap.end()) { return; } - util::ptr<TileData> data = it->second.lock(); - if (!data) { + util::ptr<TileData> tileData = it->second.lock(); + if (!tileData) { return; } - if (data->getState() == TileData::State::obsolete && !data->getError().empty()) { - emitTileLoadingFailed(data->getError()); + if (tileData->getState() == TileData::State::obsolete && !tileData->getError().empty()) { + emitTileLoadingFailed(tileData->getError()); return; } - data->redoPlacement({ transformState.getAngle(), transformState.getPitch(), collisionDebug }); + tileData->redoPlacement({ transformState.getAngle(), transformState.getPitch(), collisionDebug }); emitTileLoaded(true); } @@ -603,4 +600,4 @@ void Source::dumpDebugLogs() const { } } -} +} // namespace mbgl diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 661aa09e847..4d985091f5f 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -22,9 +22,7 @@ namespace mbgl { -class MapData; -class TexturePool; -class Style; +class StyleUpdateParameters; class Painter; class FileRequest; class TransformState; @@ -72,11 +70,7 @@ class Source : private util::noncopyable { // will return true if all the tiles were scheduled for updating of false if // they were not. shouldReparsePartialTiles must be set to "true" if there is // new data available that a tile in the "partial" state might be interested at. - bool update(MapData&, - const TransformState&, - Style&, - TexturePool&, - bool shouldReparsePartialTiles); + bool update(const StyleUpdateParameters&); void updateMatrices(const mat4 &projMatrix, const TransformState &transform); void drawClippingMasks(Painter &painter); @@ -95,7 +89,7 @@ class Source : private util::noncopyable { bool enabled; private: - void tileLoadingCompleteCallback(const TileID& normalized_id, const TransformState& transformState, bool collisionDebug); + void tileLoadingCompleteCallback(const TileID&, const TransformState&, bool collisionDebug); void emitSourceLoaded(); void emitSourceLoadingFailed(const std::string& message); @@ -108,13 +102,8 @@ class Source : private util::noncopyable { int32_t coveringZoomLevel(const TransformState&) const; std::forward_list<TileID> coveringTiles(const TransformState&) const; - TileData::State addTile(MapData&, - const TransformState&, - Style&, - TexturePool&, - const TileID&); - - TileData::State hasTile(const TileID& id); + TileData::State addTile(const TileID&, const StyleUpdateParameters&); + TileData::State hasTile(const TileID&); void updateTilePtrs(); double getZoom(const TransformState &state) const; @@ -126,13 +115,13 @@ class Source : private util::noncopyable { std::map<TileID, std::unique_ptr<Tile>> tiles; std::vector<Tile*> tilePtrs; - std::map<TileID, std::weak_ptr<TileData>> tile_data; + std::map<TileID, std::weak_ptr<TileData>> tileDataMap; TileCache cache; std::unique_ptr<FileRequest> req; Observer* observer_ = nullptr; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/tile.hpp b/src/mbgl/map/tile.hpp index 996433b5d9b..8b9030f1bd2 100644 --- a/src/mbgl/map/tile.hpp +++ b/src/mbgl/map/tile.hpp @@ -23,6 +23,6 @@ class Tile : private util::noncopyable { util::ptr<TileData> data; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/tile_cache.cpp b/src/mbgl/map/tile_cache.cpp index 37acf628a0b..2d1a0da96c4 100644 --- a/src/mbgl/map/tile_cache.cpp +++ b/src/mbgl/map/tile_cache.cpp @@ -61,4 +61,4 @@ void TileCache::clear() { tiles.clear(); } -}; +} // namespace mbgl diff --git a/src/mbgl/map/tile_cache.hpp b/src/mbgl/map/tile_cache.hpp index 26d830c123f..e39db0ffae3 100644 --- a/src/mbgl/map/tile_cache.hpp +++ b/src/mbgl/map/tile_cache.hpp @@ -25,6 +25,6 @@ class TileCache { size_t size; }; -}; +} // namespace mbgl #endif diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp index 2a5745142d4..ed3426cf80e 100644 --- a/src/mbgl/map/tile_data.hpp +++ b/src/mbgl/map/tile_data.hpp @@ -2,6 +2,7 @@ #define MBGL_MAP_TILE_DATA #include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/map/tile_id.hpp> #include <mbgl/renderer/bucket.hpp> #include <mbgl/text/placement_config.hpp> @@ -94,6 +95,8 @@ class TileData : private util::noncopyable { void dumpDebugLogs() const; const TileID id; + Seconds modified = Seconds::zero(); + Seconds expires = Seconds::zero(); // Contains the tile ID string for painting debug information. std::unique_ptr<DebugBucket> debugBucket; diff --git a/src/mbgl/map/tile_id.cpp b/src/mbgl/map/tile_id.cpp index ad7ec2e0f66..3058125a20b 100644 --- a/src/mbgl/map/tile_id.cpp +++ b/src/mbgl/map/tile_id.cpp @@ -63,4 +63,4 @@ TileID::operator std::string() const { return util::toString(z) + "/" + util::toString(x) + "/" + util::toString(y); } -} +} // namespace mbgl diff --git a/src/mbgl/map/tile_id.hpp b/src/mbgl/map/tile_id.hpp index f2e2171f1a2..dddbce3bb71 100644 --- a/src/mbgl/map/tile_id.hpp +++ b/src/mbgl/map/tile_id.hpp @@ -54,6 +54,6 @@ class TileID { }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/tile_worker.cpp b/src/mbgl/map/tile_worker.cpp index 9dc0698db7c..177e8cdd71d 100644 --- a/src/mbgl/map/tile_worker.cpp +++ b/src/mbgl/map/tile_worker.cpp @@ -1,7 +1,6 @@ #include <mbgl/text/collision_tile.hpp> #include <mbgl/map/tile_worker.hpp> #include <mbgl/map/geometry_tile.hpp> -#include <mbgl/style/style.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_bucket_parameters.hpp> #include <mbgl/sprite/sprite_atlas.hpp> @@ -10,24 +9,29 @@ #include <mbgl/platform/log.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/exception.hpp> +#include <utility> using namespace mbgl; TileWorker::TileWorker(TileID id_, std::string sourceID_, - Style& style_, + SpriteStore& spriteStore_, + GlyphAtlas& glyphAtlas_, + GlyphStore& glyphStore_, const std::atomic<TileData::State>& state_) : id(id_), - sourceID(sourceID_), - style(style_), + sourceID(std::move(sourceID_)), + spriteStore(spriteStore_), + glyphAtlas(glyphAtlas_), + glyphStore(glyphStore_), state(state_) { } TileWorker::~TileWorker() { - style.glyphAtlas->removeGlyphs(reinterpret_cast<uintptr_t>(this)); + glyphAtlas.removeGlyphs(reinterpret_cast<uintptr_t>(this)); } -TileParseResult TileWorker::parseAllLayers(std::vector<util::ptr<StyleLayer>> layers, +TileParseResult TileWorker::parseAllLayers(std::vector<std::unique_ptr<StyleLayer>> layers, const GeometryTile& geometryTile, PlacementConfig config) { // We're doing a fresh parse of the tile, because the underlying data has changed. @@ -42,10 +46,10 @@ TileParseResult TileWorker::parseAllLayers(std::vector<util::ptr<StyleLayer>> la std::set<std::string> parsed; for (auto i = layers.rbegin(); i != layers.rend(); i++) { - const StyleLayer& layer = **i; - if (parsed.find(layer.bucketName()) == parsed.end()) { - parsed.emplace(layer.bucketName()); - parseLayer(layer, geometryTile); + std::unique_ptr<StyleLayer> layer = std::move(*i); + if (parsed.find(layer->bucketName()) == parsed.end()) { + parsed.emplace(layer->bucketName()); + parseLayer(std::move(layer), geometryTile); } } @@ -57,15 +61,19 @@ TileParseResult TileWorker::parsePendingLayers() { // Try parsing the remaining layers that we couldn't parse in the first step due to missing // dependencies. for (auto it = pending.begin(); it != pending.end();) { - auto& layer = it->first; + auto& layer = *it->first; auto& bucket = it->second; assert(bucket); if (layer.type == StyleLayerType::Symbol) { auto symbolBucket = dynamic_cast<SymbolBucket*>(bucket.get()); - if (!symbolBucket->needsDependencies(*style.glyphStore, *style.spriteStore)) { - symbolBucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas, - *style.glyphAtlas, *style.glyphStore, *collisionTile); + if (!symbolBucket->needsDependencies(glyphStore, spriteStore)) { + const SymbolLayer* symbolLayer = dynamic_cast<const SymbolLayer*>(&layer); + symbolBucket->addFeatures(reinterpret_cast<uintptr_t>(this), + *symbolLayer->spriteAtlas, + glyphAtlas, + glyphStore, + *collisionTile); insertBucket(layer.bucketName(), std::move(bucket)); pending.erase(it++); continue; @@ -81,7 +89,7 @@ TileParseResult TileWorker::parsePendingLayers() { } void TileWorker::redoPlacement( - std::vector<util::ptr<StyleLayer>> layers, + std::vector<std::unique_ptr<StyleLayer>> layers, const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets, PlacementConfig config) { @@ -96,29 +104,29 @@ void TileWorker::redoPlacement( } } -void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometryTile) { +void TileWorker::parseLayer(std::unique_ptr<StyleLayer> layer, const GeometryTile& geometryTile) { // Cancel early when parsing. if (state == TileData::State::obsolete) return; // Background is a special case. - if (layer.type == StyleLayerType::Background) + if (layer->type == StyleLayerType::Background) return; // Skip this bucket if we are to not render this - if ((layer.source != sourceID) || - (id.z < std::floor(layer.minZoom)) || - (id.z >= std::ceil(layer.maxZoom)) || - (layer.visibility == VisibilityType::None)) { + if ((layer->source != sourceID) || + (id.z < std::floor(layer->minZoom)) || + (id.z >= std::ceil(layer->maxZoom)) || + (layer->visibility == VisibilityType::None)) { return; } - auto geometryLayer = geometryTile.getLayer(layer.sourceLayer); + auto geometryLayer = geometryTile.getLayer(layer->sourceLayer); if (!geometryLayer) { // The layer specified in the bucket does not exist. Do nothing. if (debug::tileParseWarnings) { Log::Warning(Event::ParseTile, "layer '%s' does not exist in tile %d/%d/%d", - layer.sourceLayer.c_str(), id.z, id.x, id.y); + layer->sourceLayer.c_str(), id.z, id.x, id.y); } return; } @@ -128,19 +136,18 @@ void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometr state, reinterpret_cast<uintptr_t>(this), partialParse, - *style.spriteAtlas, - *style.spriteStore, - *style.glyphAtlas, - *style.glyphStore, + spriteStore, + glyphAtlas, + glyphStore, *collisionTile); - std::unique_ptr<Bucket> bucket = layer.createBucket(parameters); + std::unique_ptr<Bucket> bucket = layer->createBucket(parameters); - if (layer.type == StyleLayerType::Symbol && partialParse) { + if (layer->type == StyleLayerType::Symbol && partialParse) { // We cannot parse this bucket yet. Instead, we're saving it for later. - pending.emplace_back(layer, std::move(bucket)); + pending.emplace_back(std::move(layer), std::move(bucket)); } else { - insertBucket(layer.bucketName(), std::move(bucket)); + insertBucket(layer->bucketName(), std::move(bucket)); } } diff --git a/src/mbgl/map/tile_worker.hpp b/src/mbgl/map/tile_worker.hpp index ee94f756ed0..21e0ec8a329 100644 --- a/src/mbgl/map/tile_worker.hpp +++ b/src/mbgl/map/tile_worker.hpp @@ -18,7 +18,9 @@ namespace mbgl { class CollisionTile; class GeometryTile; -class Style; +class SpriteStore; +class GlyphAtlas; +class GlyphStore; class Bucket; class StyleLayer; @@ -37,28 +39,32 @@ class TileWorker : public util::noncopyable { public: TileWorker(TileID, std::string sourceID, - Style&, + SpriteStore&, + GlyphAtlas&, + GlyphStore&, const std::atomic<TileData::State>&); ~TileWorker(); - TileParseResult parseAllLayers(std::vector<util::ptr<StyleLayer>>, + TileParseResult parseAllLayers(std::vector<std::unique_ptr<StyleLayer>>, const GeometryTile&, PlacementConfig); TileParseResult parsePendingLayers(); - void redoPlacement(std::vector<util::ptr<StyleLayer>>, + void redoPlacement(std::vector<std::unique_ptr<StyleLayer>>, const std::unordered_map<std::string, std::unique_ptr<Bucket>>*, PlacementConfig); private: - void parseLayer(const StyleLayer&, const GeometryTile&); + void parseLayer(std::unique_ptr<StyleLayer>, const GeometryTile&); void insertBucket(const std::string& name, std::unique_ptr<Bucket>); const TileID id; const std::string sourceID; - Style& style; + SpriteStore& spriteStore; + GlyphAtlas& glyphAtlas; + GlyphStore& glyphStore; const std::atomic<TileData::State>& state; bool partialParse = false; @@ -67,7 +73,7 @@ class TileWorker : public util::noncopyable { // Contains buckets that we couldn't parse so far due to missing resources. // They will be attempted on subsequent parses. - std::list<std::pair<const StyleLayer&, std::unique_ptr<Bucket>>> pending; + std::list<std::pair<std::unique_ptr<StyleLayer>, std::unique_ptr<Bucket>>> pending; // Temporary holder TileParseResultBuckets result; diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 7f424eba2b4..914fa164da7 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -88,6 +88,6 @@ class Transform : private util::noncopyable { std::function<void()> transitionFinishFn; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/vector_tile.cpp b/src/mbgl/map/vector_tile.cpp index 8b069326152..00307f04a9f 100644 --- a/src/mbgl/map/vector_tile.cpp +++ b/src/mbgl/map/vector_tile.cpp @@ -6,6 +6,7 @@ #include <mbgl/util/thread_context.hpp> #include <sstream> +#include <utility> namespace mbgl { @@ -130,7 +131,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { } VectorTile::VectorTile(std::shared_ptr<const std::string> data_) - : data(data_) { + : data(std::move(data_)) { } util::ptr<GeometryTileLayer> VectorTile::getLayer(const std::string& name) const { @@ -164,7 +165,7 @@ VectorTileLayer::VectorTileLayer(pbf layer_pbf) { } else if (layer_pbf.tag == 3) { // keys keys.emplace(layer_pbf.string(), keys.size()); } else if (layer_pbf.tag == 4) { // values - values.emplace_back(std::move(parseValue(layer_pbf.message()))); + values.emplace_back(parseValue(layer_pbf.message())); } else if (layer_pbf.tag == 5) { // extent extent = layer_pbf.varint(); } else { @@ -181,7 +182,7 @@ VectorTileMonitor::VectorTileMonitor(const SourceInfo& source, const TileID& id, : url(source.tileURL(id, pixelRatio)) { } -std::unique_ptr<FileRequest> VectorTileMonitor::monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)> callback) { +std::unique_ptr<FileRequest> VectorTileMonitor::monitorTile(const GeometryTileMonitor::Callback& callback) { return util::ThreadContext::getFileSource()->request({ Resource::Kind::Tile, url }, [callback, this](Response res) { if (res.data && data == res.data) { // We got the same data again. Abort early. @@ -190,19 +191,19 @@ std::unique_ptr<FileRequest> VectorTileMonitor::monitorTile(std::function<void ( if (res.error) { if (res.error->reason == Response::Error::Reason::NotFound) { - callback(nullptr, nullptr); + callback(nullptr, nullptr, res.modified, res.expires); return; } else { std::stringstream message; message << "Failed to load [" << url << "]: " << res.error->message; - callback(std::make_exception_ptr(std::runtime_error(message.str())), nullptr); + callback(std::make_exception_ptr(std::runtime_error(message.str())), nullptr, res.modified, res.expires); return; } } data = res.data; - callback(nullptr, std::make_unique<VectorTile>(data)); + callback(nullptr, std::make_unique<VectorTile>(data), res.modified, res.expires); }); } -} +} // namespace mbgl diff --git a/src/mbgl/map/vector_tile.hpp b/src/mbgl/map/vector_tile.hpp index 7c7f96abbde..61b97bc9a54 100644 --- a/src/mbgl/map/vector_tile.hpp +++ b/src/mbgl/map/vector_tile.hpp @@ -63,13 +63,13 @@ class VectorTileMonitor : public GeometryTileMonitor { public: VectorTileMonitor(const SourceInfo&, const TileID&, float pixelRatio); - std::unique_ptr<FileRequest> monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)>) override; + std::unique_ptr<FileRequest> monitorTile(const GeometryTileMonitor::Callback&) override; private: std::string url; std::shared_ptr<const std::string> data; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index 28be9627aec..bac13b38832 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -20,12 +20,17 @@ VectorTileData::VectorTileData(const TileID& id_, worker(style_.workers), tileWorker(id_, sourceID, - style_, + *style_.spriteStore, + *style_.glyphAtlas, + *style_.glyphStore, state), monitor(std::move(monitor_)) { state = State::loading; - tileRequest = monitor->monitorTile([callback, this](std::exception_ptr err, std::unique_ptr<GeometryTile> tile) { + tileRequest = monitor->monitorTile([callback, this](std::exception_ptr err, + std::unique_ptr<GeometryTile> tile, + Seconds modified_, + Seconds expires_) { if (err) { try { std::rethrow_exception(err); @@ -50,11 +55,14 @@ VectorTileData::VectorTileData(const TileID& id_, state = State::partial; } + modified = modified_; + expires = expires_; + // Kick off a fresh parse of this tile. This happens when the tile is new, or // when tile data changed. Replacing the workdRequest will cancel a pending work // request in case there is one. workRequest.reset(); - workRequest = worker.parseGeometryTile(tileWorker, style.layers, std::move(tile), targetConfig, [callback, this, config = targetConfig] (TileParseResult result) { + workRequest = worker.parseGeometryTile(tileWorker, style.getLayers(), std::move(tile), targetConfig, [callback, this, config = targetConfig] (TileParseResult result) { workRequest.reset(); if (state == State::obsolete) { return; @@ -158,7 +166,7 @@ void VectorTileData::redoPlacement(const PlacementConfig newConfig) { void VectorTileData::redoPlacement() { workRequest.reset(); - workRequest = worker.redoPlacement(tileWorker, style.layers, buckets, targetConfig, [this, config = targetConfig] { + workRequest = worker.redoPlacement(tileWorker, style.getLayers(), buckets, targetConfig, [this, config = targetConfig] { workRequest.reset(); // Persist the configuration we just placed so that we can later check whether we need to @@ -183,4 +191,4 @@ void VectorTileData::cancel() { workRequest.reset(); } -} +} // namespace mbgl diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp index efbe0f16728..9e3352daa89 100644 --- a/src/mbgl/map/view.cpp +++ b/src/mbgl/map/view.cpp @@ -19,4 +19,4 @@ void View::notifyMapChange(MapChange) { } -} +} // namespace mbgl diff --git a/src/mbgl/platform/gl.cpp b/src/mbgl/platform/gl.cpp index abf3d59ee55..e5495c80dcb 100644 --- a/src/mbgl/platform/gl.cpp +++ b/src/mbgl/platform/gl.cpp @@ -62,8 +62,8 @@ void checkError(const char *cmd, const char *file, int line) { throw ::mbgl::gl::Error(err, std::string(cmd) + ": Error GL_" + error + " - " + file + ":" + util::toString(line)); } } -} -} +} // namespace gl +} // namespace mbgl #ifdef GL_TRACK #undef glBindTexture diff --git a/src/mbgl/platform/log.cpp b/src/mbgl/platform/log.cpp index 6daed408882..580c6986990 100644 --- a/src/mbgl/platform/log.cpp +++ b/src/mbgl/platform/log.cpp @@ -12,7 +12,7 @@ namespace { static std::unique_ptr<Log::Observer> currentObserver; -} +} // namespace void Log::setObserver(std::unique_ptr<Observer> observer) { currentObserver = std::move(observer); @@ -63,4 +63,4 @@ void Log::record(EventSeverity severity, Event event, int64_t code, const std::s platformRecord(severity, logStream.str()); } -} +} // namespace mbgl diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp index bfe715a1574..b7cbfdf0fe4 100644 --- a/src/mbgl/renderer/bucket.hpp +++ b/src/mbgl/renderer/bucket.hpp @@ -30,7 +30,7 @@ class Bucket : private util::noncopyable { // once or twice (for Opaque and Transparent render passes). virtual void render(Painter&, const StyleLayer&, const TileID&, const mat4&) = 0; - virtual ~Bucket() {} + virtual ~Bucket() = default; virtual bool hasData() const = 0; @@ -46,6 +46,6 @@ class Bucket : private util::noncopyable { }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp index 63562a67149..a1a24eab73d 100644 --- a/src/mbgl/renderer/debug_bucket.cpp +++ b/src/mbgl/renderer/debug_bucket.cpp @@ -1,6 +1,7 @@ #include <mbgl/renderer/debug_bucket.hpp> #include <mbgl/renderer/painter.hpp> #include <mbgl/shader/plain_shader.hpp> +#include <mbgl/util/time.hpp> #include <mbgl/platform/gl.hpp> @@ -9,9 +10,25 @@ using namespace mbgl; -DebugBucket::DebugBucket(const TileID id, const TileData::State state_) : state(state_) { - const std::string text = std::string(id) + " - " + TileData::StateToString(state); - fontBuffer.addText(text.c_str(), 50, 200, 5); +DebugBucket::DebugBucket(const TileID id, const TileData::State state_, Seconds modified_, Seconds expires_, MapDebugOptions debugMode_) + : state(state_), + modified(modified_), + expires(expires_), + debugMode(debugMode_) { + double baseline = 200; + if (debugMode & MapDebugOptions::ParseStatus) { + const std::string text = std::string(id) + " - " + TileData::StateToString(state); + fontBuffer.addText(text.c_str(), 50, baseline, 5); + baseline += 200; + } + + if (debugMode & MapDebugOptions::Timestamps && modified > Seconds::zero() && expires > Seconds::zero()) { + const std::string modifiedText = "modified: " + util::iso8601(modified.count()); + fontBuffer.addText(modifiedText.c_str(), 50, baseline, 5); + + const std::string expiresText = "expires: " + util::iso8601(expires.count()); + fontBuffer.addText(expiresText.c_str(), 50, baseline + 200, 5); + } } void DebugBucket::drawLines(PlainShader& shader) { diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/debug_bucket.hpp index a5b163c0a30..73af338c2ca 100644 --- a/src/mbgl/renderer/debug_bucket.hpp +++ b/src/mbgl/renderer/debug_bucket.hpp @@ -2,8 +2,10 @@ #define MBGL_RENDERER_DEBUGBUCKET #include <mbgl/map/tile_data.hpp> +#include <mbgl/map/mode.hpp> #include <mbgl/geometry/debug_font_buffer.hpp> #include <mbgl/geometry/vao.hpp> +#include <mbgl/util/chrono.hpp> namespace mbgl { @@ -11,18 +13,21 @@ class PlainShader; class DebugBucket : private util::noncopyable { public: - DebugBucket(TileID id, TileData::State); + DebugBucket(TileID id, TileData::State, Seconds modified, Seconds expires, MapDebugOptions); void drawLines(PlainShader& shader); void drawPoints(PlainShader& shader); const TileData::State state; + const Seconds modified; + const Seconds expires; + const MapDebugOptions debugMode; private: DebugFontBuffer fontBuffer; VertexArrayObject array; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/fill_bucket.hpp index 054194340b8..674c41f7d1c 100644 --- a/src/mbgl/renderer/fill_bucket.hpp +++ b/src/mbgl/renderer/fill_bucket.hpp @@ -63,6 +63,6 @@ class FillBucket : public Bucket { static const int vertices_per_group = 3; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp index 9193183f0d6..8f6c06a6974 100644 --- a/src/mbgl/renderer/frame_history.hpp +++ b/src/mbgl/renderer/frame_history.hpp @@ -35,6 +35,6 @@ class FrameHistory { std::deque<FrameSnapshot> history; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/renderer/gl_config.cpp b/src/mbgl/renderer/gl_config.cpp index 7c6eae02139..04858782974 100644 --- a/src/mbgl/renderer/gl_config.cpp +++ b/src/mbgl/renderer/gl_config.cpp @@ -21,5 +21,5 @@ const Program::Type Program::Default = 0; const LineWidth::Type LineWidth::Default = 1; const Viewport::Type Viewport::Default = { 0, 0, 0, 0 }; -} -} +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/renderer/gl_config.hpp b/src/mbgl/renderer/gl_config.hpp index b110f071fdd..55590cf6655 100644 --- a/src/mbgl/renderer/gl_config.hpp +++ b/src/mbgl/renderer/gl_config.hpp @@ -112,9 +112,10 @@ struct ColorMask { MBGL_CHECK_ERROR(glColorMask(value.r, value.g, value.b, value.a)); } inline static Type Get() { - GLfloat floats[4]; - MBGL_CHECK_ERROR(glGetFloatv(GL_COLOR_WRITEMASK, floats)); - return { floats[0], floats[1], floats[2], floats[3] }; + GLboolean bools[4]; + MBGL_CHECK_ERROR(glGetBooleanv(GL_COLOR_WRITEMASK, bools)); + return { static_cast<bool>(bools[0]), static_cast<bool>(bools[1]), + static_cast<bool>(bools[2]), static_cast<bool>(bools[3]) }; } }; diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp index 43bb8132de2..368c4ce69d7 100644 --- a/src/mbgl/renderer/line_bucket.cpp +++ b/src/mbgl/renderer/line_bucket.cpp @@ -187,8 +187,9 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { addCurrentVertex(currentVertex, flip, distance, joinNormal, 0, 0, false, startVertex, triangleStore); - flip = -flip; + addCurrentVertex(currentVertex, -flip, distance, joinNormal, 0, 0, false, startVertex, + triangleStore); } else if (middleVertex && (currentJoin == JoinType::Bevel || currentJoin == JoinType::FakeRound)) { const bool lineTurnsLeft = flip * (prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x) > 0; const float offset = -std::sqrt(miterLength * miterLength - 1); @@ -334,7 +335,7 @@ void LineBucket::addCurrentVertex(const Coordinate& currentVertex, vec2<double> extrude = normal * flip; if (endLeft) extrude = extrude - (util::perp(normal) * endLeft); - e3 = vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 0, distance) + e3 = vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 0, endLeft, distance) - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -345,7 +346,7 @@ void LineBucket::addCurrentVertex(const Coordinate& currentVertex, extrude = normal * (-flip); if (endRight) extrude = extrude - (util::perp(normal) * endRight); - e3 = vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 1, distance) + e3 = vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 1, -endRight, distance) - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -364,7 +365,7 @@ void LineBucket::addPieSliceVertex(const Coordinate& currentVertex, int8_t ty = lineTurnsLeft; auto flippedExtrude = extrude * (flip * (lineTurnsLeft ? -1 : 1)); - e3 = vertexBuffer.add(currentVertex.x, currentVertex.y, flippedExtrude.x, flippedExtrude.y, 0, ty, distance) + e3 = vertexBuffer.add(currentVertex.x, currentVertex.y, flippedExtrude.x, flippedExtrude.y, 0, ty, 0, distance) - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp index 9a38edbc319..7d662aac021 100644 --- a/src/mbgl/renderer/line_bucket.hpp +++ b/src/mbgl/renderer/line_bucket.hpp @@ -64,6 +64,6 @@ class LineBucket : public Bucket { std::vector<std::unique_ptr<TriangleGroup>> triangleGroups; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index a3902fece06..f678414d0f4 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -45,81 +45,30 @@ using namespace mbgl; Painter::Painter(MapData& data_, TransformState& state_) : data(data_), state(state_) { - setup(); -} - -Painter::~Painter() { -} - -bool Painter::needsAnimation() const { - return frameHistory.needsAnimation(data.getDefaultFadeDuration()); -} - -void Painter::setup() { gl::debugging::enable(); - setupShaders(); - - assert(iconShader); - assert(plainShader); - assert(outlineShader); - assert(lineShader); - assert(linepatternShader); - assert(patternShader); - assert(rasterShader); - assert(sdfGlyphShader); - assert(sdfIconShader); - assert(dotShader); - assert(circleShader); + plainShader = std::make_unique<PlainShader>(); + outlineShader = std::make_unique<OutlineShader>(); + lineShader = std::make_unique<LineShader>(); + linesdfShader = std::make_unique<LineSDFShader>(); + linepatternShader = std::make_unique<LinepatternShader>(); + patternShader = std::make_unique<PatternShader>(); + iconShader = std::make_unique<IconShader>(); + rasterShader = std::make_unique<RasterShader>(); + sdfGlyphShader = std::make_unique<SDFGlyphShader>(); + sdfIconShader = std::make_unique<SDFIconShader>(); + dotShader = std::make_unique<DotShader>(); + collisionBoxShader = std::make_unique<CollisionBoxShader>(); + circleShader = std::make_unique<CircleShader>(); // Reset GL values config.reset(); } -void Painter::setupShaders() { - if (!plainShader) plainShader = std::make_unique<PlainShader>(); - if (!outlineShader) outlineShader = std::make_unique<OutlineShader>(); - if (!lineShader) lineShader = std::make_unique<LineShader>(); - if (!linesdfShader) linesdfShader = std::make_unique<LineSDFShader>(); - if (!linepatternShader) linepatternShader = std::make_unique<LinepatternShader>(); - if (!patternShader) patternShader = std::make_unique<PatternShader>(); - if (!iconShader) iconShader = std::make_unique<IconShader>(); - if (!rasterShader) rasterShader = std::make_unique<RasterShader>(); - if (!sdfGlyphShader) sdfGlyphShader = std::make_unique<SDFGlyphShader>(); - if (!sdfIconShader) sdfIconShader = std::make_unique<SDFIconShader>(); - if (!dotShader) dotShader = std::make_unique<DotShader>(); - if (!collisionBoxShader) collisionBoxShader = std::make_unique<CollisionBoxShader>(); - if (!circleShader) circleShader = std::make_unique<CircleShader>(); -} - -void Painter::resize() { - config.viewport = { 0, 0, frame.framebufferSize[0], frame.framebufferSize[1] }; -} - -void Painter::changeMatrix() { - - state.getProjMatrix(projMatrix); - - // The extrusion matrix. - matrix::ortho(extrudeMatrix, 0, state.getWidth(), state.getHeight(), 0, 0, -1); +Painter::~Painter() = default; - // The native matrix is a 1:1 matrix that paints the coordinates at the - // same screen position as the vertex specifies. - matrix::identity(nativeMatrix); - matrix::multiply(nativeMatrix, projMatrix, nativeMatrix); -} - -void Painter::clear() { - MBGL_DEBUG_GROUP("clear"); - config.stencilFunc.reset(); - config.stencilTest = GL_TRUE; - config.stencilMask = 0xFF; - config.depthTest = GL_FALSE; - config.depthMask = GL_TRUE; - config.clearColor = { background[0], background[1], background[2], background[3] }; - config.clearStencil = 0; - config.clearDepth = 1; - MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); +bool Painter::needsAnimation() const { + return frameHistory.needsAnimation(data.getDefaultFadeDuration()); } void Painter::prepareTile(const Tile& tile) { @@ -128,25 +77,30 @@ void Painter::prepareTile(const Tile& tile) { config.stencilFunc = { GL_EQUAL, ref, mask }; } -void Painter::render(const Style& style, const FrameData& frame_) { +void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& annotationSpriteAtlas) { frame = frame_; glyphAtlas = style.glyphAtlas.get(); spriteAtlas = style.spriteAtlas.get(); lineAtlas = style.lineAtlas.get(); - std::set<Source*> sources; - for (const auto& source : style.sources) { - if (source->enabled) { - sources.insert(source.get()); - } - } + RenderData renderData = style.getRenderData(); + const std::vector<RenderItem>& order = renderData.order; + const std::set<Source*>& sources = renderData.sources; + const Color& background = renderData.backgroundColor; + + config.viewport = { 0, 0, frame.framebufferSize[0], frame.framebufferSize[1] }; - resize(); - changeMatrix(); + // Update the default matrices to the current viewport dimensions. + state.getProjMatrix(projMatrix); - // Figure out what buckets we have to draw and what order we have to draw them in. - const auto order = determineRenderOrder(style); + // The extrusion matrix. + matrix::ortho(extrudeMatrix, 0, state.getWidth(), state.getHeight(), 0, 0, -1); + + // The native matrix is a 1:1 matrix that paints the coordinates at the + // same screen position as the vertex specifies. + matrix::identity(nativeMatrix); + matrix::multiply(nativeMatrix, projMatrix, nativeMatrix); // - UPLOAD PASS ------------------------------------------------------------------------------- // Uploads all required buffers and images before we do any actual rendering. @@ -158,6 +112,7 @@ void Painter::render(const Style& style, const FrameData& frame_) { spriteAtlas->upload(); lineAtlas->upload(); glyphAtlas->upload(); + annotationSpriteAtlas.upload(); for (const auto& item : order) { if (item.bucket && item.bucket->needsUpload()) { @@ -166,6 +121,21 @@ void Painter::render(const Style& style, const FrameData& frame_) { } } + // - CLEAR ------------------------------------------------------------------------------------- + // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any + // tiles whatsoever. + { + MBGL_DEBUG_GROUP("clear"); + config.stencilFunc.reset(); + config.stencilTest = GL_TRUE; + config.stencilMask = 0xFF; + config.depthTest = GL_FALSE; + config.depthMask = GL_TRUE; + config.clearColor = { background[0], background[1], background[2], background[3] }; + config.clearStencil = 0; + config.clearDepth = 1; + MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + } // - CLIPPING MASKS ---------------------------------------------------------------------------- // Draws the clipping masks to the stencil buffer. @@ -179,8 +149,6 @@ void Painter::render(const Style& style, const FrameData& frame_) { source->updateMatrices(projMatrix, state); } - clear(); - drawClippingMasks(sources); } @@ -274,100 +242,26 @@ void Painter::renderPass(RenderPass pass_, } } -std::vector<RenderItem> Painter::determineRenderOrder(const Style& style) { - std::vector<RenderItem> order; - - for (const auto& layerPtr : style.layers) { - const auto& layer = *layerPtr; - if (layer.visibility == VisibilityType::None) continue; - if (layer.type == StyleLayerType::Background) { - // This layer defines a background color/image. - auto& props = dynamic_cast<const BackgroundLayer&>(layer).paint; - if (props.pattern.value.from.empty()) { - // This is a solid background. We can use glClear(). - background = props.color; - background[0] *= props.opacity; - background[1] *= props.opacity; - background[2] *= props.opacity; - background[3] *= props.opacity; - } else { - // This is a textured background. We need to render it with a quad. - background = {{ 0, 0, 0, 0 }}; - order.emplace_back(layer); - } - continue; - } - - Source* source = style.getSource(layer.source); - if (!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.minZoom > zoom || - layer.maxZoom <= zoom) { - continue; - } - - const auto& tiles = source->getTiles(); - for (auto tile : tiles) { - assert(tile); - if (!tile->data && !tile->data->isReady()) { - continue; - } - - // We're not clipping symbol layers, so when we have both parents and children of symbol - // layers, we drop all children in favor of their parent to avoid duplicate labels. - // See https://github.com/mapbox/mapbox-gl-native/issues/2482 - if (layer.type == StyleLayerType::Symbol) { - bool skip = false; - // Look back through the buckets we decided to render to find out whether there is - // already a bucket from this layer that is a parent of this tile. Tiles are ordered - // by zoom level when we obtain them from getTiles(). - for (auto it = order.rbegin(); it != order.rend() && (&it->layer == &layer); ++it) { - if (tile->id.isChildOf(it->tile->id)) { - skip = true; - break; - } - } - if (skip) { - continue; - } - } - - auto bucket = tile->data->getBucket(layer); - if (bucket) { - order.emplace_back(layer, tile, bucket); - } - } - } - - return order; -} - void Painter::renderBackground(const BackgroundLayer& layer) { // Note: This function is only called for textured background. Otherwise, the background color // is created with glClear. const BackgroundPaintProperties& properties = layer.paint; if (!properties.pattern.value.to.empty()) { - if ((properties.opacity >= 1.0f) != (pass == RenderPass::Opaque)) + mapbox::util::optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true); + mapbox::util::optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true); + + if ((properties.opacity >= 1.0f) != (pass == RenderPass::Opaque) || !imagePosA || !imagePosB) return; - SpriteAtlasPosition imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true); - SpriteAtlasPosition imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true); float zoomFraction = state.getZoomFraction(); config.program = patternShader->program; patternShader->u_matrix = identityMatrix; - patternShader->u_pattern_tl_a = imagePosA.tl; - patternShader->u_pattern_br_a = imagePosA.br; - patternShader->u_pattern_tl_b = imagePosB.tl; - patternShader->u_pattern_br_b = imagePosB.br; + patternShader->u_pattern_tl_a = (*imagePosA).tl; + patternShader->u_pattern_br_a = (*imagePosA).br; + patternShader->u_pattern_tl_b = (*imagePosB).tl; + patternShader->u_pattern_br_b = (*imagePosB).br; patternShader->u_mix = properties.pattern.value.t; patternShader->u_opacity = properties.opacity; @@ -375,7 +269,7 @@ void Painter::renderBackground(const BackgroundLayer& layer) { PrecisionPoint center = state.latLngToPoint(latLng); float scale = 1 / std::pow(2, zoomFraction); - std::array<float, 2> sizeA = imagePosA.size; + std::array<float, 2> sizeA = (*imagePosA).size; mat3 matrixA; matrix::identity(matrixA); matrix::scale(matrixA, matrixA, @@ -389,7 +283,7 @@ void Painter::renderBackground(const BackgroundLayer& layer) { scale * state.getWidth() / 2, -scale * state.getHeight() / 2); - std::array<float, 2> sizeB = imagePosB.size; + std::array<float, 2> sizeB = (*imagePosB).size; mat3 matrixB; matrix::identity(matrixB); matrix::scale(matrixB, matrixB, diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 5de1031d23c..7189bcc19be 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -34,7 +34,6 @@ class LineAtlas; class Source; struct FrameData; - class DebugBucket; class FillBucket; class FillLayer; @@ -48,8 +47,6 @@ class RasterBucket; class RasterLayer; class BackgroundLayer; -struct RasterProperties; - class SDFShader; class PlainShader; class OutlineShader; @@ -68,32 +65,14 @@ class CollisionBoxShader; struct ClipID; -struct RenderItem { - inline RenderItem(const StyleLayer& layer_, - const Tile* tile_ = nullptr, - Bucket* bucket_ = nullptr) - : tile(tile_), bucket(bucket_), layer(layer_) { - } - - const Tile* const tile; - Bucket* const bucket; - const StyleLayer& layer; -}; - class Painter : private util::noncopyable { public: Painter(MapData&, TransformState&); ~Painter(); - // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any - // tiles whatsoever. - void clear(); - - // Updates the default matrices to the current viewport dimensions. - void changeMatrix(); - void render(const Style& style, - const FrameData& frame); + const FrameData& frame, + SpriteAtlas& annotationSpriteAtlas); // Renders debug information for a tile. void renderTileDebug(const Tile& tile); @@ -113,29 +92,12 @@ class Painter : private util::noncopyable { float contrastFactor(float contrast); std::array<float, 3> spinWeights(float spin_value); - void preparePrerender(RasterBucket &bucket); - - void renderPrerenderedTexture(RasterBucket &bucket, const mat4 &matrix, const RasterProperties& properties); - - void createPrerendered(RasterBucket& bucket, const StyleLayer &layer_desc, const TileID& id); - - // Adjusts the dimensions of the OpenGL viewport - void resize(); - void drawClippingMasks(const std::set<Source*>&); void drawClippingMask(const mat4& matrix, const ClipID& clip); - void resetFramebuffer(); - void bindFramebuffer(); - void pushFramebuffer(); - GLuint popFramebuffer(); - void discardFramebuffers(); - bool needsAnimation() const; private: - void setup(); - void setupShaders(); mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor); std::vector<RenderItem> determineRenderOrder(const Style& style); @@ -160,7 +122,6 @@ class Painter : private util::noncopyable { void setDepthSublayer(int n); -public: mat4 projMatrix; mat4 nativeMatrix; mat4 extrudeMatrix; @@ -179,7 +140,6 @@ class Painter : private util::noncopyable { return identity; }(); -private: MapData& data; TransformState& state; FrameData frame; @@ -189,20 +149,18 @@ class Painter : private util::noncopyable { gl::Config config; RenderPass pass = RenderPass::Opaque; - Color background = {{ 0, 0, 0, 0 }}; int numSublayers = 3; GLsizei currentLayer; float depthRangeSize; const float depthEpsilon = 1.0f / (1 << 16); -public: - FrameHistory frameHistory; - SpriteAtlas* spriteAtlas; GlyphAtlas* glyphAtlas; LineAtlas* lineAtlas; + FrameHistory frameHistory; + std::unique_ptr<PlainShader> plainShader; std::unique_ptr<OutlineShader> outlineShader; std::unique_ptr<LineShader> lineShader; @@ -252,6 +210,6 @@ class Painter : private util::noncopyable { VertexArrayObject tileBorderArray; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp index b981dae260c..0a0900526a6 100644 --- a/src/mbgl/renderer/painter_debug.cpp +++ b/src/mbgl/renderer/painter_debug.cpp @@ -12,7 +12,7 @@ using namespace mbgl; void Painter::renderTileDebug(const Tile& tile) { MBGL_DEBUG_GROUP(std::string { "debug " } + std::string(tile.id)); assert(tile.data); - if (data.getDebug()) { + if (data.getDebug() != MapDebugOptions::NoDebug) { prepareTile(tile); renderDebugText(*tile.data, tile.matrix); renderDebugFrame(tile.matrix); @@ -24,8 +24,11 @@ void Painter::renderDebugText(TileData& tileData, const mat4 &matrix) { config.depthTest = GL_FALSE; - if (!tileData.debugBucket || tileData.debugBucket->state != tileData.getState()) { - tileData.debugBucket = std::make_unique<DebugBucket>(tileData.id, tileData.getState()); + if (!tileData.debugBucket || tileData.debugBucket->state != tileData.getState() + || tileData.debugBucket->modified != tileData.modified + || tileData.debugBucket->expires != tileData.expires + || tileData.debugBucket->debugMode != data.getDebug()) { + tileData.debugBucket = std::make_unique<DebugBucket>(tileData.id, tileData.getState(), tileData.modified, tileData.expires, data.getDebug()); } config.program = plainShader->program; diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 4bee5b6d1e6..beebdfcf83d 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -59,30 +59,30 @@ void Painter::renderFill(FillBucket& bucket, const FillLayer& layer, const TileI } if (pattern) { - // Image fill. - if (pass == RenderPass::Translucent) { + mapbox::util::optional<SpriteAtlasPosition> posA = spriteAtlas->getPosition(properties.pattern.value.from, true); + mapbox::util::optional<SpriteAtlasPosition> posB = spriteAtlas->getPosition(properties.pattern.value.to, true); - const SpriteAtlasPosition posA = spriteAtlas->getPosition(properties.pattern.value.from, true); - const SpriteAtlasPosition posB = spriteAtlas->getPosition(properties.pattern.value.to, true); + // Image fill. + if (pass == RenderPass::Translucent && posA && posB) { float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z) / id.overscaling; mat3 patternMatrixA; matrix::identity(patternMatrixA); matrix::scale(patternMatrixA, patternMatrixA, - 1.0f / (posA.size[0] * factor * properties.pattern.value.fromScale), - 1.0f / (posA.size[1] * factor * properties.pattern.value.fromScale)); + 1.0f / ((*posA).size[0] * factor * properties.pattern.value.fromScale), + 1.0f / ((*posA).size[1] * factor * properties.pattern.value.fromScale)); mat3 patternMatrixB; matrix::identity(patternMatrixB); matrix::scale(patternMatrixB, patternMatrixB, - 1.0f / (posB.size[0] * factor * properties.pattern.value.toScale), - 1.0f / (posB.size[1] * factor * properties.pattern.value.toScale)); + 1.0f / ((*posB).size[0] * factor * properties.pattern.value.toScale), + 1.0f / ((*posB).size[1] * factor * properties.pattern.value.toScale)); config.program = patternShader->program; patternShader->u_matrix = vtxMatrix; - patternShader->u_pattern_tl_a = posA.tl; - patternShader->u_pattern_br_a = posA.br; - patternShader->u_pattern_tl_b = posB.tl; - patternShader->u_pattern_br_b = posB.br; + patternShader->u_pattern_tl_a = (*posA).tl; + patternShader->u_pattern_br_a = (*posA).br; + patternShader->u_pattern_tl_b = (*posB).tl; + patternShader->u_pattern_br_b = (*posB).br; patternShader->u_opacity = properties.opacity; patternShader->u_image = 0; patternShader->u_mix = properties.pattern.value.t; diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp index e33864622e0..d9d2d9fc5dd 100644 --- a/src/mbgl/renderer/painter_line.cpp +++ b/src/mbgl/renderer/painter_line.cpp @@ -97,13 +97,17 @@ void Painter::renderLine(LineBucket& bucket, const LineLayer& layer, const TileI linesdfShader->u_sdfgamma = lineAtlas->width / (properties.dashLineWidth * std::min(posA.width, posB.width) * 256.0 * data.pixelRatio) / 2; linesdfShader->u_mix = properties.dasharray.value.t; linesdfShader->u_extra = extra; + linesdfShader->u_offset = properties.offset; linesdfShader->u_antialiasingmatrix = antialiasingMatrix; bucket.drawLineSDF(*linesdfShader); } else if (!properties.pattern.value.from.empty()) { - SpriteAtlasPosition imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true); - SpriteAtlasPosition imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true); + mapbox::util::optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true); + mapbox::util::optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true); + + if (!imagePosA || !imagePosB) + return; float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z) * id.overscaling; @@ -115,15 +119,16 @@ void Painter::renderLine(LineBucket& bucket, const LineLayer& layer, const TileI linepatternShader->u_ratio = ratio; linepatternShader->u_blur = blur; - linepatternShader->u_pattern_size_a = {{imagePosA.size[0] * factor * properties.pattern.value.fromScale, imagePosA.size[1]}}; - linepatternShader->u_pattern_tl_a = imagePosA.tl; - linepatternShader->u_pattern_br_a = imagePosA.br; - linepatternShader->u_pattern_size_b = {{imagePosB.size[0] * factor * properties.pattern.value.toScale, imagePosB.size[1]}}; - linepatternShader->u_pattern_tl_b = imagePosB.tl; - linepatternShader->u_pattern_br_b = imagePosB.br; + linepatternShader->u_pattern_size_a = {{(*imagePosA).size[0] * factor * properties.pattern.value.fromScale, (*imagePosA).size[1]}}; + linepatternShader->u_pattern_tl_a = (*imagePosA).tl; + linepatternShader->u_pattern_br_a = (*imagePosA).br; + linepatternShader->u_pattern_size_b = {{(*imagePosB).size[0] * factor * properties.pattern.value.toScale, (*imagePosB).size[1]}}; + linepatternShader->u_pattern_tl_b = (*imagePosB).tl; + linepatternShader->u_pattern_br_b = (*imagePosB).br; linepatternShader->u_fade = properties.pattern.value.t; linepatternShader->u_opacity = properties.opacity; linepatternShader->u_extra = extra; + linepatternShader->u_offset = properties.offset; linepatternShader->u_antialiasingmatrix = antialiasingMatrix; MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); @@ -140,6 +145,7 @@ void Painter::renderLine(LineBucket& bucket, const LineLayer& layer, const TileI lineShader->u_ratio = ratio; lineShader->u_blur = blur; lineShader->u_extra = extra; + lineShader->u_offset = properties.offset; lineShader->u_antialiasingmatrix = antialiasingMatrix; lineShader->u_color = color; diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index f46a9c84543..4a00d941db4 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -192,7 +192,8 @@ void Painter::renderSymbol(SymbolBucket& bucket, const SymbolLayer& layer, const const float fontSize = properties.icon.size; const float fontScale = fontSize / 1.0f; - spriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line + SpriteAtlas* activeSpriteAtlas = layer.spriteAtlas; + activeSpriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line || angleOffset != 0 || fontScale != 1 || sdf || state.getPitch() != 0); if (sdf) { @@ -202,7 +203,7 @@ void Painter::renderSymbol(SymbolBucket& bucket, const SymbolLayer& layer, const layout.icon, properties.icon, 1.0f, - {{ float(spriteAtlas->getWidth()) / 4.0f, float(spriteAtlas->getHeight()) / 4.0f }}, + {{ float(activeSpriteAtlas->getWidth()) / 4.0f, float(activeSpriteAtlas->getHeight()) / 4.0f }}, *sdfIconShader, &SymbolBucket::drawIcons); } else { diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp index e12257d864c..0f4263ec959 100644 --- a/src/mbgl/renderer/raster_bucket.hpp +++ b/src/mbgl/renderer/raster_bucket.hpp @@ -25,6 +25,6 @@ class RasterBucket : public Bucket { Raster raster; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp index a298b8b57e5..a1e965d9118 100644 --- a/src/mbgl/renderer/render_pass.hpp +++ b/src/mbgl/renderer/render_pass.hpp @@ -26,6 +26,6 @@ constexpr inline RenderPass operator&(RenderPass a, RenderPass b) { static_cast<std::underlying_type<RenderPass>::type>(b)); } -} +} // namespace mbgl #endif diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 1f2ad96bf25..92fdaaf5710 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -247,10 +247,10 @@ void SymbolBucket::addFeatures(uintptr_t tileUID, // if feature has icon, get sprite atlas position if (feature.sprite.length()) { auto image = spriteAtlas.getImage(feature.sprite, false); - if (image.pos.hasArea() && image.texture) { - shapedIcon = shapeIcon(image.pos, layout); - assert(image.texture); - if (image.texture->sdf) { + if (image) { + shapedIcon = shapeIcon((*image).pos, layout); + assert((*image).texture); + if ((*image).texture->sdf) { sdfIcons = true; } } @@ -616,4 +616,4 @@ void SymbolBucket::drawCollisionBoxes(CollisionBoxShader &shader) { MBGL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, group->vertex_length)); } } -} +} // namespace mbgl diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index ff4bbf5dcab..7150c9808c5 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -147,6 +147,6 @@ class SymbolBucket : public Bucket { std::unique_ptr<SymbolRenderData> renderDataInProgress; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/circle_shader.hpp b/src/mbgl/shader/circle_shader.hpp index 84f4ef99a1e..7c1212c2752 100644 --- a/src/mbgl/shader/circle_shader.hpp +++ b/src/mbgl/shader/circle_shader.hpp @@ -19,6 +19,6 @@ class CircleShader : public Shader { Uniform<GLfloat> u_blur = {"u_blur", *this}; }; -} +} // namespace mbgl #endif // MBGL_SHADER_CIRCLE_SHADER diff --git a/src/mbgl/shader/dot_shader.hpp b/src/mbgl/shader/dot_shader.hpp index fe1541cf753..606764c2d65 100644 --- a/src/mbgl/shader/dot_shader.hpp +++ b/src/mbgl/shader/dot_shader.hpp @@ -18,6 +18,6 @@ class DotShader : public Shader { Uniform<GLfloat> u_blur = {"u_blur", *this}; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/icon_shader.hpp b/src/mbgl/shader/icon_shader.hpp index 437fd859bcb..df6232cdcc2 100644 --- a/src/mbgl/shader/icon_shader.hpp +++ b/src/mbgl/shader/icon_shader.hpp @@ -30,6 +30,6 @@ class IconShader : public Shader { GLint a_data2 = -1; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/line.vertex.glsl b/src/mbgl/shader/line.vertex.glsl index 980212384bb..1bd7f5ef5f4 100644 --- a/src/mbgl/shader/line.vertex.glsl +++ b/src/mbgl/shader/line.vertex.glsl @@ -14,6 +14,7 @@ uniform mat4 u_matrix; // shared uniform float u_ratio; uniform vec2 u_linewidth; +uniform float u_offset; uniform float u_extra; uniform mat2 u_antialiasingmatrix; @@ -23,6 +24,7 @@ varying float v_gamma_scale; void main() { vec2 a_extrude = a_data.xy; + float a_direction = sign(a_data.z) * mod(a_data.z, 2.0); // We store the texture normals in the most insignificant bit // transform y so that 0 => -1 and 1 => 1 @@ -36,11 +38,19 @@ void main() { // of this vertex. vec2 dist = u_linewidth.s * a_extrude * scale; + // Calculate the offset when drawing a line that is to the side of the actual line. + // We do this by creating a vector that points towards the extrude, but rotate + // it when we're drawing round end points (a_direction = -1 or 1) since their + // extrude vector points in another direction. + float u = 0.5 * a_direction; + float t = 1.0 - abs(u); + vec2 offset = u_offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + // Remove the texture normal bit of the position before scaling it with the // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist / u_ratio, 0.0, 1.0); + gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + (offset + dist) / u_ratio, 0.0, 1.0); // position of y on the screen float y = gl_Position.y / gl_Position.w; diff --git a/src/mbgl/shader/line_shader.hpp b/src/mbgl/shader/line_shader.hpp index 580035a07a3..14e03c2eafb 100644 --- a/src/mbgl/shader/line_shader.hpp +++ b/src/mbgl/shader/line_shader.hpp @@ -19,6 +19,7 @@ class LineShader : public Shader { Uniform<GLfloat> u_ratio = {"u_ratio", *this}; Uniform<GLfloat> u_blur = {"u_blur", *this}; Uniform<GLfloat> u_extra = {"u_extra", *this}; + Uniform<GLfloat> u_offset = {"u_offset", *this}; UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this}; private: @@ -26,6 +27,6 @@ class LineShader : public Shader { }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/linepattern.vertex.glsl b/src/mbgl/shader/linepattern.vertex.glsl index 541c9fc3a7c..5b3c468be0c 100644 --- a/src/mbgl/shader/linepattern.vertex.glsl +++ b/src/mbgl/shader/linepattern.vertex.glsl @@ -17,6 +17,7 @@ uniform mat4 u_exmatrix; // shared uniform float u_ratio; uniform vec2 u_linewidth; +uniform float u_offset; uniform vec4 u_color; uniform float u_extra; @@ -28,7 +29,8 @@ varying float v_gamma_scale; void main() { vec2 a_extrude = a_data.xy; - float a_linesofar = a_data.z * 128.0 + a_data.w; + float a_direction = sign(a_data.z) * mod(a_data.z, 2.0); + float a_linesofar = abs(floor(a_data.z / 2.0)) + a_data.w * 64.0; // We store the texture normals in the most insignificant bit // transform y so that 0 => -1 and 1 => 1 @@ -42,11 +44,19 @@ void main() { // of this vertex. vec2 dist = u_linewidth.s * a_extrude * scale; + // Calculate the offset when drawing a line that is to the side of the actual line. + // We do this by creating a vector that points towards the extrude, but rotate + // it when we're drawing round end points (a_direction = -1 or 1) since their + // extrude vector points in another direction. + float u = 0.5 * a_direction; + float t = 1.0 - abs(u); + vec2 offset = u_offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + // Remove the texture normal bit of the position before scaling it with the // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos / 2.0) + dist.xy / u_ratio, 0.0, 1.0); + gl_Position = u_matrix * vec4(floor(a_pos / 2.0) + (offset + dist) / u_ratio, 0.0, 1.0); v_linesofar = a_linesofar; // position of y on the screen diff --git a/src/mbgl/shader/linepattern_shader.hpp b/src/mbgl/shader/linepattern_shader.hpp index f2189d4710a..6c83cb0cc3f 100644 --- a/src/mbgl/shader/linepattern_shader.hpp +++ b/src/mbgl/shader/linepattern_shader.hpp @@ -27,11 +27,12 @@ class LinepatternShader : public Shader { Uniform<GLfloat> u_fade = {"u_fade", *this}; Uniform<GLfloat> u_opacity = {"u_opacity", *this}; Uniform<GLfloat> u_extra = {"u_extra", *this}; + Uniform<GLfloat> u_offset = {"u_offset", *this}; UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this}; private: GLint a_data = -1; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/linesdf.vertex.glsl b/src/mbgl/shader/linesdf.vertex.glsl index 03851ed533d..80516f97c00 100644 --- a/src/mbgl/shader/linesdf.vertex.glsl +++ b/src/mbgl/shader/linesdf.vertex.glsl @@ -17,6 +17,7 @@ uniform mat4 u_exmatrix; // shared uniform float u_ratio; uniform vec2 u_linewidth; +uniform float u_offset; uniform vec2 u_patternscale_a; uniform float u_tex_y_a; uniform vec2 u_patternscale_b; @@ -32,7 +33,8 @@ varying float v_gamma_scale; void main() { vec2 a_extrude = a_data.xy; - float a_linesofar = a_data.z * 128.0 + a_data.w; + float a_direction = sign(a_data.z) * mod(a_data.z, 2.0); + float a_linesofar = abs(floor(a_data.z / 2.0)) + a_data.w * 64.0; // We store the texture normals in the most insignificant bit // transform y so that 0 => -1 and 1 => 1 @@ -46,11 +48,19 @@ void main() { // of this vertex. vec2 dist = u_linewidth.s * a_extrude * scale; + // Calculate the offset when drawing a line that is to the side of the actual line. + // We do this by creating a vector that points towards the extrude, but rotate + // it when we're drawing round end points (a_direction = -1 or 1) since their + // extrude vector points in another direction. + float u = 0.5 * a_direction; + float t = 1.0 - abs(u); + vec2 offset = u_offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + // Remove the texture normal bit of the position before scaling it with the // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist / u_ratio, 0.0, 1.0); + gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + (offset + dist) / u_ratio, 0.0, 1.0); v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a); v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b); diff --git a/src/mbgl/shader/linesdf_shader.hpp b/src/mbgl/shader/linesdf_shader.hpp index 839494f79bc..2a8bdc6629d 100644 --- a/src/mbgl/shader/linesdf_shader.hpp +++ b/src/mbgl/shader/linesdf_shader.hpp @@ -26,6 +26,7 @@ class LineSDFShader : public Shader { Uniform<GLfloat> u_sdfgamma = {"u_sdfgamma", *this}; Uniform<GLfloat> u_mix = {"u_mix", *this}; Uniform<GLfloat> u_extra = {"u_extra", *this}; + Uniform<GLfloat> u_offset = {"u_offset", *this}; UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this}; private: @@ -33,6 +34,6 @@ class LineSDFShader : public Shader { }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/outline_shader.hpp b/src/mbgl/shader/outline_shader.hpp index 2cd4b1dc31e..3af9b8349ff 100644 --- a/src/mbgl/shader/outline_shader.hpp +++ b/src/mbgl/shader/outline_shader.hpp @@ -17,6 +17,6 @@ class OutlineShader : public Shader { Uniform<std::array<GLfloat, 2>> u_world = {"u_world", *this}; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/pattern_shader.hpp b/src/mbgl/shader/pattern_shader.hpp index 3dd3daa4618..d47cbfb182e 100644 --- a/src/mbgl/shader/pattern_shader.hpp +++ b/src/mbgl/shader/pattern_shader.hpp @@ -24,6 +24,6 @@ class PatternShader : public Shader { UniformMatrix<3> u_patternmatrix_b = {"u_patternmatrix_b", *this}; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/plain_shader.hpp b/src/mbgl/shader/plain_shader.hpp index ee0528e9e3c..f9b8c41a630 100644 --- a/src/mbgl/shader/plain_shader.hpp +++ b/src/mbgl/shader/plain_shader.hpp @@ -16,6 +16,6 @@ class PlainShader : public Shader { Uniform<std::array<GLfloat, 4>> u_color = {"u_color", *this}; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/raster_shader.hpp b/src/mbgl/shader/raster_shader.hpp index af51b4ba464..60b283fc2a7 100644 --- a/src/mbgl/shader/raster_shader.hpp +++ b/src/mbgl/shader/raster_shader.hpp @@ -23,6 +23,6 @@ class RasterShader : public Shader { Uniform<std::array<GLfloat, 3>> u_spin_weights = {"u_spin_weights", *this}; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/sdf_shader.hpp b/src/mbgl/shader/sdf_shader.hpp index fcb2e15d112..aa888d7d4b7 100644 --- a/src/mbgl/shader/sdf_shader.hpp +++ b/src/mbgl/shader/sdf_shader.hpp @@ -40,6 +40,6 @@ class SDFIconShader : public SDFShader { void bind(GLbyte *offset) final; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/shader.hpp b/src/mbgl/shader/shader.hpp index 1de56da87ae..927fdf0b0b0 100644 --- a/src/mbgl/shader/shader.hpp +++ b/src/mbgl/shader/shader.hpp @@ -34,6 +34,6 @@ class Shader : private util::noncopyable { GLuint fragShader = 0; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/shader/uniform.cpp b/src/mbgl/shader/uniform.cpp index eca1a99d176..d43dd20016f 100644 --- a/src/mbgl/shader/uniform.cpp +++ b/src/mbgl/shader/uniform.cpp @@ -44,4 +44,4 @@ void UniformMatrix<4>::bind(const std::array<GLfloat, 16>& t) { // Add more as needed. -} +} // namespace mbgl diff --git a/src/mbgl/shader/uniform.hpp b/src/mbgl/shader/uniform.hpp index 55646f7d87a..0b6d574d09b 100644 --- a/src/mbgl/shader/uniform.hpp +++ b/src/mbgl/shader/uniform.hpp @@ -56,6 +56,6 @@ class UniformMatrix { GLint location; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp index 45b0511c68c..b04d8edc5ff 100644 --- a/src/mbgl/sprite/sprite_atlas.cpp +++ b/src/mbgl/sprite/sprite_atlas.cpp @@ -24,9 +24,7 @@ SpriteAtlas::SpriteAtlas(dimension width_, dimension height_, float pixelRatio_, pixelRatio(pixelRatio_), store(store_), bin(width_, height_), - data(std::make_unique<uint32_t[]>(pixelWidth * pixelHeight)), dirty(true) { - std::fill(data.get(), data.get() + pixelWidth * pixelHeight, 0); } Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width, const size_t pixel_height) { @@ -49,17 +47,17 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width return rect; } -SpriteAtlasElement SpriteAtlas::getImage(const std::string& name, const bool wrap) { +mapbox::util::optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& name, const bool wrap) { std::lock_guard<std::recursive_mutex> lock(mtx); auto rect_it = images.find({ name, wrap }); if (rect_it != images.end()) { - return { rect_it->second.pos, rect_it->second.texture }; + return SpriteAtlasElement { rect_it->second.pos, rect_it->second.texture }; } auto sprite = store.getSprite(name); if (!sprite) { - return { Rect<dimension> { 0, 0, 0, 0 }, nullptr }; + return {}; } Rect<dimension> rect = allocateImage(sprite->width, sprite->height); @@ -67,19 +65,24 @@ SpriteAtlasElement SpriteAtlas::getImage(const std::string& name, const bool wra if (debug::spriteWarnings) { Log::Warning(Event::Sprite, "sprite atlas bitmap overflow"); } - return { Rect<dimension> { 0, 0, 0, 0 }, nullptr }; + return {}; } const Holder& holder = images.emplace(Key{ name, wrap }, Holder{ sprite, rect }).first->second; copy(holder, wrap); - return { rect, sprite }; + return SpriteAtlasElement { rect, sprite }; } -SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repeating) { +mapbox::util::optional<SpriteAtlasPosition> SpriteAtlas::getPosition(const std::string& name, bool repeating) { std::lock_guard<std::recursive_mutex> lock(mtx); - auto rect = getImage(name, repeating).pos; + auto img = getImage(name, repeating); + if (!img) { + return {}; + } + + auto rect = (*img).pos; if (repeating) { // When the image is repeating, get the correct position of the image, rather than the // one rounded up to 4 pixels. @@ -103,6 +106,11 @@ SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repea } void SpriteAtlas::copy(const Holder& holder, const bool wrap) { + if (!data) { + data = std::make_unique<uint32_t[]>(pixelWidth * pixelHeight); + std::fill(data.get(), data.get() + pixelWidth * pixelHeight, 0); + } + const uint32_t *srcData = reinterpret_cast<const uint32_t *>(holder.texture->data.data()); if (!srcData) return; const vec2<uint32_t> srcSize { holder.texture->pixelWidth, holder.texture->pixelHeight }; @@ -179,8 +187,15 @@ void SpriteAtlas::updateDirty() { // The two names match; Holder& holder = imageIterator->second; holder.texture = spriteIterator->second; - copy(holder, imageIterator->first.second); - + if (holder.texture != nullptr) + { + copy(holder, imageIterator->first.second); + } + else + { + images.erase(imageIterator); + } + ++imageIterator; // Don't advance the spriteIterator because there might be another sprite with the same // name, but a different wrap value. @@ -189,6 +204,10 @@ void SpriteAtlas::updateDirty() { } void SpriteAtlas::bind(bool linear) { + if (!data) { + return; // Empty atlas + } + if (!texture) { MBGL_CHECK_ERROR(glGenTextures(1, &texture)); MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp index 0d86279e2d4..167d012c6b4 100644 --- a/src/mbgl/sprite/sprite_atlas.hpp +++ b/src/mbgl/sprite/sprite_atlas.hpp @@ -6,6 +6,8 @@ #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/ptr.hpp> +#include <mapbox/optional.hpp> + #include <string> #include <map> #include <mutex> @@ -41,15 +43,12 @@ class SpriteAtlas : public util::noncopyable { SpriteAtlas(dimension width, dimension height, float pixelRatio, SpriteStore& store); ~SpriteAtlas(); - // Returns the coordinates of an image that is sourced from the sprite image. - // This getter attempts to read the image from the sprite if it is already loaded. - // In that case, it copies it into the sprite atlas and returns the dimensions. - // Otherwise, it returns a 0/0/0/0 rect. - // This function is used during bucket creation. - SpriteAtlasElement getImage(const std::string& name, const bool wrap); + // If the sprite is loaded, copies the requsted image from it into the atlas and returns + // the resulting icon measurements. If not, returns an empty optional. + mapbox::util::optional<SpriteAtlasElement> getImage(const std::string& name, const bool wrap); // This function is used for getting the position during render time. - SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false); + mapbox::util::optional<SpriteAtlasPosition> getPosition(const std::string& name, bool repeating = false); // Binds the atlas texture to the GPU, and uploads data if it is out of date. void bind(bool linear = false); @@ -66,6 +65,8 @@ class SpriteAtlas : public util::noncopyable { inline dimension getTextureWidth() const { return pixelWidth; } inline dimension getTextureHeight() const { return pixelHeight; } inline float getPixelRatio() const { return pixelRatio; } + + // Only for use in tests. inline const uint32_t* getData() const { return data.get(); } private: @@ -90,7 +91,7 @@ class SpriteAtlas : public util::noncopyable { BinPack<dimension> bin; std::map<Key, Holder> images; std::set<std::string> uninitialized; - const std::unique_ptr<uint32_t[]> data; + std::unique_ptr<uint32_t[]> data; std::atomic<bool> dirty; bool fullUploadRequired = true; GLuint texture = 0; @@ -98,6 +99,6 @@ class SpriteAtlas : public util::noncopyable { static const int buffer = 1; }; -}; +} // namespace mbgl #endif diff --git a/src/mbgl/sprite/sprite_store.cpp b/src/mbgl/sprite/sprite_store.cpp index 8e002290504..f54f3f18fde 100644 --- a/src/mbgl/sprite/sprite_store.cpp +++ b/src/mbgl/sprite/sprite_store.cpp @@ -6,8 +6,8 @@ #include <mbgl/storage/response.hpp> #include <mbgl/util/exception.hpp> #include <mbgl/util/thread_context.hpp> -#include <mbgl/util/run_loop.hpp> +#include <cassert> #include <string> #include <sstream> diff --git a/src/mbgl/storage/asset_context_base.hpp b/src/mbgl/storage/asset_context_base.hpp index cd7d9ff4a10..4a6ca0b6f9d 100644 --- a/src/mbgl/storage/asset_context_base.hpp +++ b/src/mbgl/storage/asset_context_base.hpp @@ -3,18 +3,15 @@ #include <mbgl/storage/request_base.hpp> -typedef struct uv_loop_s uv_loop_t; - namespace mbgl { class AssetContextBase { public: - static std::unique_ptr<AssetContextBase> createContext(uv_loop_t*); + static std::unique_ptr<AssetContextBase> createContext(); virtual ~AssetContextBase() = default; virtual RequestBase* createRequest(const Resource&, RequestBase::Callback, - uv_loop_t*, const std::string& assetRoot) = 0; }; diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp index dd8f8370ece..367c0833608 100644 --- a/src/mbgl/storage/default_file_source.cpp +++ b/src/mbgl/storage/default_file_source.cpp @@ -7,7 +7,6 @@ #include <mbgl/platform/platform.hpp> #include <mbgl/platform/log.hpp> -#include <mbgl/util/uv_detail.hpp> #include <mbgl/util/thread.hpp> #include <mbgl/util/mapbox.hpp> #include <mbgl/util/exception.hpp> @@ -78,21 +77,20 @@ void DefaultFileSource::cancel(const Resource& res, FileRequest* req) { // ----- Impl ----- DefaultFileSource::Impl::Impl(FileCache* cache_, const std::string& root) - : loop(util::RunLoop::getLoop()), - cache(cache_), + : cache(cache_), assetRoot(root.empty() ? platform::assetRoot() : root), - assetContext(AssetContextBase::createContext(loop)), - httpContext(HTTPContextBase::createContext(loop)), - reachability(std::make_unique<uv::async>(loop, std::bind(&Impl::networkIsReachableAgain, this))) { + assetContext(AssetContextBase::createContext()), + httpContext(HTTPContextBase::createContext()), + reachability(std::bind(&Impl::networkIsReachableAgain, this)) { // Subscribe to network status changes, but make sure that this async handle doesn't keep the // loop alive; otherwise our app wouldn't terminate. After all, we only need status change // notifications when our app is still running. - NetworkStatus::Subscribe(reachability->get()); - reachability->unref(); + NetworkStatus::Subscribe(&reachability); + reachability.unref(); } DefaultFileSource::Impl::~Impl() { - NetworkStatus::Unsubscribe(reachability->get()); + NetworkStatus::Unsubscribe(&reachability); } void DefaultFileSource::Impl::networkIsReachableAgain() { @@ -202,10 +200,10 @@ void DefaultFileSource::Impl::startRealRequest(DefaultFileRequestImpl& request) if (algo::starts_with(request.resource.url, "asset://")) { request.realRequest = - assetContext->createRequest(request.resource, callback, loop, assetRoot); + assetContext->createRequest(request.resource, callback, assetRoot); } else { request.realRequest = - httpContext->createRequest(request.resource, callback, loop, request.getResponse()); + httpContext->createRequest(request.resource, callback, request.getResponse()); } } @@ -231,17 +229,16 @@ void DefaultFileSource::Impl::reschedule(DefaultFileRequestImpl& request) { return; } - const auto timeout = request.getRetryTimeout(); + const Seconds timeout = request.getRetryTimeout(); - if (timeout == 0) { + if (timeout == Seconds::zero()) { update(request); - } else if (timeout > 0) { + } else if (timeout > Seconds::zero()) { if (!request.timerRequest) { - request.timerRequest = std::make_unique<uv::timer>(util::RunLoop::getLoop()); + request.timerRequest = std::make_unique<util::Timer>(); } - // timeout is in seconds, but the timer takes milliseconds. - request.timerRequest->start(1000 * timeout, 0, [this, &request] { + request.timerRequest->start(timeout, Duration::zero(), [this, &request] { assert(!request.realRequest); startRealRequest(request); }); @@ -298,14 +295,16 @@ const std::shared_ptr<const Response>& DefaultFileRequestImpl::getResponse() con return response; } -int64_t DefaultFileRequestImpl::getRetryTimeout() const { +Seconds DefaultFileRequestImpl::getRetryTimeout() const { + Seconds timeout = Seconds::zero(); + if (!response) { // If we don't have a response, we should retry immediately. - return 0; + return timeout; } // A value < 0 means that we should not retry. - int64_t timeout = -1; + timeout = Seconds(-1); if (response->error) { assert(failedRequests > 0); @@ -314,14 +313,14 @@ int64_t DefaultFileRequestImpl::getRetryTimeout() const { // Retry immediately, unless we have a certain number of attempts already const int graceRetries = 3; if (failedRequests <= graceRetries) { - timeout = 1; + timeout = Seconds(1); } else { - timeout = 1 << std::min(failedRequests - graceRetries, 31); + timeout = Seconds(1 << std::min(failedRequests - graceRetries, 31)); } } break; case Response::Error::Reason::Connection: { // Exponential backoff - timeout = 1 << std::min(failedRequests - 1, 31); + timeout = Seconds(1 << std::min(failedRequests - 1, 31)); } break; default: // Do not retry due to error. @@ -330,14 +329,11 @@ int64_t DefaultFileRequestImpl::getRetryTimeout() const { } // Check to see if this response expires earlier than a potential error retry. - if (response->expires > 0) { - const int64_t expires = - response->expires - - std::chrono::duration_cast<std::chrono::seconds>(SystemClock::now().time_since_epoch()) - .count(); + if (response->expires > Seconds::zero()) { + const Seconds secsToExpire = response->expires - toSeconds(SystemClock::now()); // Only update the timeout if we don't have one yet, and only if the new timeout is shorter // than the previous one. - timeout = timeout < 0 ? expires : std::min(timeout, std::max<int64_t>(0, expires)); + timeout = timeout < Seconds::zero() ? secsToExpire: std::min(timeout, std::max(Seconds::zero(), secsToExpire)); } return timeout; diff --git a/src/mbgl/storage/default_file_source_impl.hpp b/src/mbgl/storage/default_file_source_impl.hpp index c063a7d1f82..9b5f97332a9 100644 --- a/src/mbgl/storage/default_file_source_impl.hpp +++ b/src/mbgl/storage/default_file_source_impl.hpp @@ -4,7 +4,10 @@ #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/asset_context_base.hpp> #include <mbgl/storage/http_context_base.hpp> +#include <mbgl/util/async_task.hpp> #include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/timer.hpp> #include <set> #include <unordered_map> @@ -38,7 +41,7 @@ class DefaultFileRequestImpl : public util::noncopyable { const Resource resource; std::unique_ptr<WorkRequest> cacheRequest; RequestBase* realRequest = nullptr; - std::unique_ptr<uv::timer> timerRequest; + std::unique_ptr<util::Timer> timerRequest; inline DefaultFileRequestImpl(const Resource& resource_) : resource(resource_) {} @@ -57,7 +60,7 @@ class DefaultFileRequestImpl : public util::noncopyable { // Returns the seconds we have to wait until we need to redo this request. A value of 0 // means that we need to redo it immediately, and a negative value means that we're not setting // a timeout at all. - int64_t getRetryTimeout() const; + Seconds getRetryTimeout() const; // Checks the currently stored response and replaces it with an idential one, except with the // stale flag set, if the response is expired. @@ -100,14 +103,13 @@ class DefaultFileSource::Impl { void reschedule(DefaultFileRequestImpl&); std::unordered_map<Resource, std::unique_ptr<DefaultFileRequestImpl>, Resource::Hash> pending; - uv_loop_t* const loop; FileCache* const cache; const std::string assetRoot; const std::unique_ptr<AssetContextBase> assetContext; const std::unique_ptr<HTTPContextBase> httpContext; - const std::unique_ptr<uv::async> reachability; + util::AsyncTask reachability; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/storage/http_context_base.cpp b/src/mbgl/storage/http_context_base.cpp index a25f1b91854..c69e7a36ae7 100644 --- a/src/mbgl/storage/http_context_base.cpp +++ b/src/mbgl/storage/http_context_base.cpp @@ -2,4 +2,4 @@ namespace mbgl { -} +} // namespace mbgl diff --git a/src/mbgl/storage/http_context_base.hpp b/src/mbgl/storage/http_context_base.hpp index fd6e444ee3e..7fbef719f66 100644 --- a/src/mbgl/storage/http_context_base.hpp +++ b/src/mbgl/storage/http_context_base.hpp @@ -4,7 +4,6 @@ #include <mbgl/storage/request_base.hpp> #include <mbgl/storage/http_request_base.hpp> #include <mbgl/storage/network_status.hpp> -#include <mbgl/util/uv_detail.hpp> #include <set> @@ -12,12 +11,11 @@ namespace mbgl { class HTTPContextBase { public: - static std::unique_ptr<HTTPContextBase> createContext(uv_loop_t*); + static std::unique_ptr<HTTPContextBase> createContext(); virtual ~HTTPContextBase() = default; virtual HTTPRequestBase* createRequest(const Resource&, RequestBase::Callback, - uv_loop_t*, std::shared_ptr<const Response>) = 0; }; diff --git a/src/mbgl/storage/http_request_base.cpp b/src/mbgl/storage/http_request_base.cpp index f9e40c4f2bf..8a4a81b291e 100644 --- a/src/mbgl/storage/http_request_base.cpp +++ b/src/mbgl/storage/http_request_base.cpp @@ -5,18 +5,15 @@ namespace mbgl { -int64_t HTTPRequestBase::parseCacheControl(const char *value) { +Seconds HTTPRequestBase::parseCacheControl(const char *value) { if (value) { const auto cacheControl = http::CacheControl::parse(value); - if (cacheControl.maxAge) { - return std::chrono::duration_cast<std::chrono::seconds>( - std::chrono::system_clock::now().time_since_epoch()).count() + - *cacheControl.maxAge; + return toSeconds(SystemClock::now()) + Seconds(*cacheControl.maxAge); } } - return 0; + return Seconds::zero(); } -} +} // namespace mbgl diff --git a/src/mbgl/storage/http_request_base.hpp b/src/mbgl/storage/http_request_base.hpp index 4c296050c74..eeefdf28d42 100644 --- a/src/mbgl/storage/http_request_base.hpp +++ b/src/mbgl/storage/http_request_base.hpp @@ -2,6 +2,7 @@ #define MBGL_STORAGE_HTTP_REQUEST_BASE #include <mbgl/storage/request_base.hpp> +#include <mbgl/util/chrono.hpp> namespace mbgl { @@ -16,7 +17,7 @@ namespace mbgl { virtual void cancel() override { cancelled = true; }; protected: - static int64_t parseCacheControl(const char *value); + static Seconds parseCacheControl(const char *value); bool cancelled; }; diff --git a/src/mbgl/storage/network_status.cpp b/src/mbgl/storage/network_status.cpp index 04b6937d943..796cc5d27f0 100644 --- a/src/mbgl/storage/network_status.cpp +++ b/src/mbgl/storage/network_status.cpp @@ -1,6 +1,6 @@ #include <mbgl/storage/network_status.hpp> -#include <uv.h> +#include <mbgl/util/async_task.hpp> // Example: Allocate a reachability object // Reachability* reach = [Reachability reachabilityForInternetConnection]; @@ -10,14 +10,14 @@ namespace mbgl { std::mutex NetworkStatus::mtx; -std::set<uv_async_t *> NetworkStatus::observers; +std::set<util::AsyncTask *> NetworkStatus::observers; -void NetworkStatus::Subscribe(uv_async_t *async) { +void NetworkStatus::Subscribe(util::AsyncTask *async) { std::lock_guard<std::mutex> lock(NetworkStatus::mtx); observers.insert(async); } -void NetworkStatus::Unsubscribe(uv_async_t *async) { +void NetworkStatus::Unsubscribe(util::AsyncTask *async) { std::lock_guard<std::mutex> lock(NetworkStatus::mtx); observers.erase(async); } @@ -25,8 +25,8 @@ void NetworkStatus::Unsubscribe(uv_async_t *async) { void NetworkStatus::Reachable() { std::lock_guard<std::mutex> lock(NetworkStatus::mtx); for (auto async : observers) { - uv_async_send(async); + async->send(); } } -} +} // namespace mbgl diff --git a/src/mbgl/storage/request_base.hpp b/src/mbgl/storage/request_base.hpp index 0f3e257ad17..5e99041cd40 100644 --- a/src/mbgl/storage/request_base.hpp +++ b/src/mbgl/storage/request_base.hpp @@ -7,6 +7,7 @@ #include <memory> #include <functional> +#include <utility> namespace mbgl { @@ -18,7 +19,7 @@ class RequestBase : private util::noncopyable { RequestBase(const Resource& resource_, Callback notify_) : resource(resource_) - , notify(notify_) { + , notify(std::move(notify_)) { } virtual ~RequestBase() = default; diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp index 1dcd397b62c..4b1125c110e 100644 --- a/src/mbgl/storage/response.cpp +++ b/src/mbgl/storage/response.cpp @@ -18,11 +18,8 @@ Response& Response::operator=(const Response& res) { } bool Response::isExpired() const { - const int64_t now = - std::chrono::duration_cast<std::chrono::seconds>(SystemClock::now().time_since_epoch()) - .count(); // Note: expires == 0 also counts as expired! - return expires <= now; + return SystemTimePoint(expires) <= SystemClock::now(); } Response::Error::Error(Reason reason_, const std::string& message_) diff --git a/src/mbgl/style/class_dictionary.cpp b/src/mbgl/style/class_dictionary.cpp index ba7c0d55be2..53ea5c1484b 100644 --- a/src/mbgl/style/class_dictionary.cpp +++ b/src/mbgl/style/class_dictionary.cpp @@ -1,14 +1,12 @@ #include <mbgl/style/class_dictionary.hpp> -#include <uv.h> +#include <pthread.h> namespace mbgl { ClassDictionary::ClassDictionary() {} ClassDictionary &ClassDictionary::Get() { - // Note: We should eventually switch to uv_key_* functions, but libuv 0.10 doesn't have these - // yet. Instead, we're using the pthread functions directly for now. static pthread_once_t store_once = PTHREAD_ONCE_INIT; static pthread_key_t store_key; @@ -48,4 +46,4 @@ ClassID ClassDictionary::normalize(ClassID id) { } } -} +} // namespace mbgl diff --git a/src/mbgl/style/class_dictionary.hpp b/src/mbgl/style/class_dictionary.hpp index ecf80be3e36..8a06a77c7a7 100644 --- a/src/mbgl/style/class_dictionary.hpp +++ b/src/mbgl/style/class_dictionary.hpp @@ -32,6 +32,6 @@ class ClassDictionary { uint32_t offset = 0; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/filter_expression.cpp b/src/mbgl/style/filter_expression.cpp index bb755c5644c..a93674410cc 100644 --- a/src/mbgl/style/filter_expression.cpp +++ b/src/mbgl/style/filter_expression.cpp @@ -125,4 +125,4 @@ FilterExpression parseFilterExpression(const rapidjson::Value& value) { } } -} +} // namespace mbgl diff --git a/src/mbgl/style/filter_expression.hpp b/src/mbgl/style/filter_expression.hpp index 8c6f447770c..7fe244632f5 100644 --- a/src/mbgl/style/filter_expression.hpp +++ b/src/mbgl/style/filter_expression.hpp @@ -120,6 +120,6 @@ struct NoneExpression { bool evaluate(const Extractor&) const; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/filter_expression_private.hpp b/src/mbgl/style/filter_expression_private.hpp index ff6301b7c64..67a37c2d4c5 100644 --- a/src/mbgl/style/filter_expression_private.hpp +++ b/src/mbgl/style/filter_expression_private.hpp @@ -116,4 +116,4 @@ bool NoneExpression::evaluate(const Extractor& extractor) const { return true; } -} +} // namespace mbgl diff --git a/src/mbgl/style/function.cpp b/src/mbgl/style/function.cpp index c656f868881..88fb1d6a9bb 100644 --- a/src/mbgl/style/function.cpp +++ b/src/mbgl/style/function.cpp @@ -136,4 +136,4 @@ Faded<T> Function<Faded<T>>::evaluate(const StyleCalculationParameters& paramete template class Function<Faded<std::string>>; template class Function<Faded<std::vector<float>>>; -} +} // namespace mbgl diff --git a/src/mbgl/style/function.hpp b/src/mbgl/style/function.hpp index 3f5c4795125..00d4319e0be 100644 --- a/src/mbgl/style/function.hpp +++ b/src/mbgl/style/function.hpp @@ -53,6 +53,6 @@ class Function<Faded<T>> { std::vector<std::pair<float, T>> stops; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/layout_property.hpp b/src/mbgl/style/layout_property.hpp index 5634efd0a3c..db2b2d3aba4 100644 --- a/src/mbgl/style/layout_property.hpp +++ b/src/mbgl/style/layout_property.hpp @@ -3,6 +3,7 @@ #include <mbgl/style/property_parsing.hpp> #include <mbgl/style/function.hpp> +#include <utility> #include <rapidjson/document.h> @@ -13,7 +14,7 @@ using JSVal = rapidjson::Value; template <typename T> class LayoutProperty { public: - LayoutProperty(T v) : value(v) {} + LayoutProperty(T v) : value(std::move(v)) {} void parse(const char * name, const JSVal& layout) { if (layout.HasMember(name)) { @@ -33,6 +34,6 @@ class LayoutProperty { T value; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp index a82bf028ba9..710ce34a4fb 100644 --- a/src/mbgl/style/paint_property.hpp +++ b/src/mbgl/style/paint_property.hpp @@ -12,6 +12,7 @@ #include <rapidjson/document.h> #include <map> +#include <utility> namespace mbgl { @@ -117,7 +118,7 @@ class PaintProperty { : prior(std::move(prior_)), begin(begin_), end(end_), - value(value_) { + value(std::move(value_)) { } Result calculate(const StyleCalculationParameters& parameters) { @@ -147,6 +148,6 @@ class PaintProperty { Result value; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/property_parsing.cpp b/src/mbgl/style/property_parsing.cpp index 9a67af8eb21..63e1fe57e98 100644 --- a/src/mbgl/style/property_parsing.cpp +++ b/src/mbgl/style/property_parsing.cpp @@ -404,4 +404,4 @@ optional<Function<Faded<std::string>>> parseProperty(const char* name, const JSV return Function<Faded<std::string>>(*constant); } -} +} // namespace mbgl diff --git a/src/mbgl/style/property_parsing.hpp b/src/mbgl/style/property_parsing.hpp index c4547a9e1eb..d287468e79a 100644 --- a/src/mbgl/style/property_parsing.hpp +++ b/src/mbgl/style/property_parsing.hpp @@ -18,6 +18,6 @@ using optional = mapbox::util::optional<T>; template <typename T> optional<T> parseProperty(const char* name, const JSVal&); -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/property_transition.hpp b/src/mbgl/style/property_transition.hpp index 1cf1de0932f..63bbf4607dd 100644 --- a/src/mbgl/style/property_transition.hpp +++ b/src/mbgl/style/property_transition.hpp @@ -14,6 +14,6 @@ class PropertyTransition { mapbox::util::optional<Duration> delay; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index 3523a72079d..bb447b9a9ec 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -1,19 +1,24 @@ #include <mbgl/style/style.hpp> #include <mbgl/map/map_data.hpp> #include <mbgl/map/source.hpp> +#include <mbgl/map/tile.hpp> #include <mbgl/map/transform_state.hpp> +#include <mbgl/layer/symbol_layer.hpp> #include <mbgl/sprite/sprite_store.hpp> #include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_parser.hpp> #include <mbgl/style/property_transition.hpp> #include <mbgl/style/class_dictionary.hpp> +#include <mbgl/style/style_update_parameters.hpp> #include <mbgl/style/style_cascade_parameters.hpp> #include <mbgl/style/style_calculation_parameters.hpp> #include <mbgl/geometry/glyph_atlas.hpp> #include <mbgl/geometry/line_atlas.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/platform/log.hpp> +#include <mbgl/layer/background_layer.hpp> + #include <csscolorparser/csscolorparser.hpp> #include <rapidjson/document.h> @@ -30,7 +35,6 @@ Style::Style(MapData& data_) spriteStore(std::make_unique<SpriteStore>(data.pixelRatio)), spriteAtlas(std::make_unique<SpriteAtlas>(512, 512, data.pixelRatio, *spriteStore)), lineAtlas(std::make_unique<LineAtlas>(512, 512)), - mtx(std::make_unique<uv::rwlock>()), workers(4) { glyphStore->setObserver(this); spriteStore->setObserver(this); @@ -47,16 +51,16 @@ void Style::setJSON(const std::string& json, const std::string&) { StyleParser parser; parser.parse(doc); - for (auto& source : parser.getSources()) { + for (auto& source : parser.sources) { addSource(std::move(source)); } - for (auto& layer : parser.getLayers()) { + for (auto& layer : parser.layers) { addLayer(std::move(layer)); } - glyphStore->setURL(parser.getGlyphURL()); - spriteStore->setURL(parser.getSpriteURL()); + glyphStore->setURL(parser.glyphURL); + spriteStore->setURL(parser.spriteURL); loaded = true; } @@ -76,7 +80,16 @@ void Style::addSource(std::unique_ptr<Source> source) { sources.emplace_back(std::move(source)); } -std::vector<util::ptr<StyleLayer>>::const_iterator Style::findLayer(const std::string& id) const { +std::vector<std::unique_ptr<StyleLayer>> Style::getLayers() const { + std::vector<std::unique_ptr<StyleLayer>> result; + result.reserve(layers.size()); + for (const auto& layer : layers) { + result.push_back(layer->clone()); + } + return result; +} + +std::vector<std::unique_ptr<StyleLayer>>::const_iterator Style::findLayer(const std::string& id) const { return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { return layer->id == id; }); @@ -87,11 +100,17 @@ StyleLayer* Style::getLayer(const std::string& id) const { return it != layers.end() ? it->get() : nullptr; } -void Style::addLayer(util::ptr<StyleLayer> layer) { +void Style::addLayer(std::unique_ptr<StyleLayer> layer) { + if (SymbolLayer* symbolLayer = dynamic_cast<SymbolLayer*>(layer.get())) { + if (!symbolLayer->spriteAtlas) { + symbolLayer->spriteAtlas = spriteAtlas.get(); + } + } + layers.emplace_back(std::move(layer)); } -void Style::addLayer(util::ptr<StyleLayer> layer, const std::string& before) { +void Style::addLayer(std::unique_ptr<StyleLayer> layer, const std::string& before) { layers.emplace(findLayer(before), std::move(layer)); } @@ -105,8 +124,18 @@ void Style::removeLayer(const std::string& id) { void Style::update(const TransformState& transform, TexturePool& texturePool) { bool allTilesUpdated = true; + StyleUpdateParameters parameters(data.pixelRatio, + data.getDebug(), + data.getAnimationTime(), + transform, + workers, + texturePool, + shouldReparsePartialTiles, + data, + *this); + for (const auto& source : sources) { - if (!source->update(data, transform, *this, texturePool, shouldReparsePartialTiles)) { + if (!source->update(parameters)) { allTilesUpdated = false; } } @@ -139,8 +168,6 @@ void Style::cascade() { } void Style::recalculate(float z) { - uv::writelock lock(mtx); - for (const auto& source : sources) { source->enabled = false; } @@ -194,6 +221,85 @@ bool Style::isLoaded() const { return true; } +RenderData Style::getRenderData() const { + RenderData result; + + for (const auto& source : sources) { + if (source->enabled) { + result.sources.insert(source.get()); + } + } + + for (const auto& layer : layers) { + if (layer->visibility == VisibilityType::None) + continue; + + if (const BackgroundLayer* background = dynamic_cast<const BackgroundLayer*>(layer.get())) { + if (background->paint.pattern.value.from.empty()) { + // This is a solid background. We can use glClear(). + result.backgroundColor = background->paint.color; + result.backgroundColor[0] *= background->paint.opacity; + result.backgroundColor[1] *= background->paint.opacity; + result.backgroundColor[2] *= background->paint.opacity; + result.backgroundColor[3] *= background->paint.opacity; + } else { + // This is a textured background. We need to render it with a quad. + result.order.emplace_back(*layer); + } + continue; + } + + Source* source = getSource(layer->source); + if (!source) { + Log::Warning(Event::Render, "can't find source for layer '%s'", layer->id.c_str()); + continue; + } + + for (auto tile : source->getTiles()) { + if (!tile->data || !tile->data->isReady()) + continue; + + // We're not clipping symbol layers, so when we have both parents and children of symbol + // layers, we drop all children in favor of their parent to avoid duplicate labels. + // See https://github.com/mapbox/mapbox-gl-native/issues/2482 + if (layer->type == StyleLayerType::Symbol) { + bool skip = false; + // Look back through the buckets we decided to render to find out whether there is + // already a bucket from this layer that is a parent of this tile. Tiles are ordered + // by zoom level when we obtain them from getTiles(). + for (auto it = result.order.rbegin(); it != result.order.rend() && (&it->layer == layer.get()); ++it) { + if (tile->id.isChildOf(it->tile->id)) { + skip = true; + break; + } + } + if (skip) { + continue; + } + } + + auto bucket = tile->data->getBucket(*layer); + if (bucket) { + result.order.emplace_back(*layer, tile, bucket); + } + } + } + + return result; +} + +void Style::setSourceTileCacheSize(size_t size) { + for (const auto& source : sources) { + source->setCacheSize(size); + } +} + +void Style::onLowMemory() { + for (const auto& source : sources) { + source->onLowMemory(); + } +} + void Style::setObserver(Observer* observer_) { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); assert(!observer); @@ -273,4 +379,4 @@ void Style::dumpDebugLogs() const { spriteStore->dumpDebugLogs(); } -} +} // namespace mbgl diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index dbaf8cf02c9..59668666792 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -1,7 +1,6 @@ #ifndef MBGL_STYLE_STYLE #define MBGL_STYLE_STYLE -#include <mbgl/style/property_transition.hpp> #include <mbgl/style/zoom_history.hpp> #include <mbgl/map/source.hpp> @@ -18,12 +17,36 @@ namespace mbgl { +class MapData; class GlyphAtlas; class GlyphStore; class SpriteStore; class SpriteAtlas; class LineAtlas; class StyleLayer; +class TransformState; +class TexturePool; + +class Tile; +class Bucket; + +struct RenderItem { + inline RenderItem(const StyleLayer& layer_, + const Tile* tile_ = nullptr, + Bucket* bucket_ = nullptr) + : tile(tile_), bucket(bucket_), layer(layer_) { + } + + const Tile* const tile; + Bucket* const bucket; + const StyleLayer& layer; +}; + +struct RenderData { + Color backgroundColor = {{ 0, 0, 0, 0 }}; + std::set<Source*> sources; + std::vector<RenderItem> order; +}; class Style : public GlyphStore::Observer, public SpriteStore::Observer, @@ -61,13 +84,19 @@ class Style : public GlyphStore::Observer, } Source* getSource(const std::string& id) const; - StyleLayer* getLayer(const std::string& id) const; - void addSource(std::unique_ptr<Source>); - void addLayer(util::ptr<StyleLayer>); - void addLayer(util::ptr<StyleLayer>, const std::string& beforeLayerID); + + std::vector<std::unique_ptr<StyleLayer>> getLayers() const; + StyleLayer* getLayer(const std::string& id) const; + void addLayer(std::unique_ptr<StyleLayer>); + void addLayer(std::unique_ptr<StyleLayer>, const std::string& beforeLayerID); void removeLayer(const std::string& layerID); + RenderData getRenderData() const; + + void setSourceTileCacheSize(size_t); + void onLowMemory(); + void dumpDebugLogs() const; MapData& data; @@ -77,11 +106,11 @@ class Style : public GlyphStore::Observer, std::unique_ptr<SpriteAtlas> spriteAtlas; std::unique_ptr<LineAtlas> lineAtlas; +private: std::vector<std::unique_ptr<Source>> sources; - std::vector<util::ptr<StyleLayer>> layers; + std::vector<std::unique_ptr<StyleLayer>> layers; -private: - std::vector<util::ptr<StyleLayer>>::const_iterator findLayer(const std::string& layerID) const; + std::vector<std::unique_ptr<StyleLayer>>::const_iterator findLayer(const std::string& layerID) const; // GlyphStore::Observer implementation. void onGlyphRangeLoaded() override; @@ -106,7 +135,6 @@ class Style : public GlyphStore::Observer, std::exception_ptr lastError; - std::unique_ptr<uv::rwlock> mtx; ZoomHistory zoomHistory; bool hasPendingTransitions = false; @@ -115,6 +143,6 @@ class Style : public GlyphStore::Observer, Worker workers; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/style_bucket_parameters.cpp b/src/mbgl/style/style_bucket_parameters.cpp index 1f9b5ab1c08..e8303e3d8fc 100644 --- a/src/mbgl/style/style_bucket_parameters.cpp +++ b/src/mbgl/style/style_bucket_parameters.cpp @@ -16,4 +16,4 @@ void StyleBucketParameters::eachFilteredFeature(const FilterExpression& filter, } } -} +} // namespace mbgl diff --git a/src/mbgl/style/style_bucket_parameters.hpp b/src/mbgl/style/style_bucket_parameters.hpp index c27d1287b22..5a84a33d48c 100644 --- a/src/mbgl/style/style_bucket_parameters.hpp +++ b/src/mbgl/style/style_bucket_parameters.hpp @@ -11,7 +11,6 @@ namespace mbgl { class TileID; class GeometryTileLayer; class GeometryTileFeature; -class SpriteAtlas; class SpriteStore; class GlyphAtlas; class GlyphStore; @@ -24,7 +23,6 @@ class StyleBucketParameters { const std::atomic<TileData::State>& state_, uintptr_t tileUID_, bool& partialParse_, - SpriteAtlas& spriteAtlas_, SpriteStore& spriteStore_, GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_, @@ -34,7 +32,6 @@ class StyleBucketParameters { state(state_), tileUID(tileUID_), partialParse(partialParse_), - spriteAtlas(spriteAtlas_), spriteStore(spriteStore_), glyphAtlas(glyphAtlas_), glyphStore(glyphStore_), @@ -51,13 +48,12 @@ class StyleBucketParameters { const std::atomic<TileData::State>& state; uintptr_t tileUID; bool& partialParse; - SpriteAtlas& spriteAtlas; SpriteStore& spriteStore; GlyphAtlas& glyphAtlas; GlyphStore& glyphStore; CollisionTile& collisionTile; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/style_calculation_parameters.hpp b/src/mbgl/style/style_calculation_parameters.hpp index 0c42d8fbfc2..00a578d4e77 100644 --- a/src/mbgl/style/style_calculation_parameters.hpp +++ b/src/mbgl/style/style_calculation_parameters.hpp @@ -26,6 +26,6 @@ class StyleCalculationParameters { Duration defaultFadeDuration; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/style_cascade_parameters.hpp b/src/mbgl/style/style_cascade_parameters.hpp index 154890fd970..f801aa52c98 100644 --- a/src/mbgl/style/style_cascade_parameters.hpp +++ b/src/mbgl/style/style_cascade_parameters.hpp @@ -24,6 +24,6 @@ class StyleCascadeParameters { PropertyTransition defaultTransition; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/style_layer.cpp b/src/mbgl/style/style_layer.cpp index 1b3d1a3c34c..3220a018a76 100644 --- a/src/mbgl/style/style_layer.cpp +++ b/src/mbgl/style/style_layer.cpp @@ -36,6 +36,8 @@ bool StyleLayer::hasRenderPass(RenderPass pass) const { } void StyleLayer::copy(const StyleLayer& src) { + id = src.id; + ref = src.ref; type = src.type; source = src.source; sourceLayer = src.sourceLayer; @@ -45,4 +47,4 @@ void StyleLayer::copy(const StyleLayer& src) { visibility = src.visibility; } -} +} // namespace mbgl diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp index a1a0389fbe6..ec29cc6790a 100644 --- a/src/mbgl/style/style_layer.hpp +++ b/src/mbgl/style/style_layer.hpp @@ -65,6 +65,6 @@ class StyleLayer : public util::noncopyable { void copy(const StyleLayer& source); }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index dc14827dc6d..097f1af941e 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -1,5 +1,4 @@ #include <mbgl/style/style_parser.hpp> -#include <mbgl/style/style_layer.hpp> #include <mbgl/platform/log.hpp> @@ -7,9 +6,11 @@ namespace mbgl { +StyleParser::~StyleParser() = default; + void StyleParser::parse(const JSVal& document) { if (document.HasMember("version")) { - version = document["version"].GetInt(); + int version = document["version"].GetInt(); if (version != 8) { Log::Warning(Event::ParseStyle, "current renderer implementation only supports style spec version 8; using an outdated style will cause rendering errors"); } @@ -134,7 +135,7 @@ void StyleParser::parseLayers(const JSVal& value) { continue; } - layersMap.emplace(layerID, std::pair<const JSVal&, util::ptr<StyleLayer>> { layerValue, nullptr }); + layersMap.emplace(layerID, std::pair<const JSVal&, std::unique_ptr<StyleLayer>> { layerValue, nullptr }); ids.push_back(layerID); } @@ -144,14 +145,18 @@ void StyleParser::parseLayers(const JSVal& value) { parseLayer(it->first, it->second.first, it->second.second); + } + + for (const auto& id : ids) { + auto it = layersMap.find(id); if (it->second.second) { - layers.emplace_back(it->second.second); + layers.emplace_back(std::move(it->second.second)); } } } -void StyleParser::parseLayer(const std::string& id, const JSVal& value, util::ptr<StyleLayer>& layer) { +void StyleParser::parseLayer(const std::string& id, const JSVal& value, std::unique_ptr<StyleLayer>& layer) { if (layer) { // Skip parsing this again. We already have a valid layer definition. return; @@ -185,7 +190,7 @@ void StyleParser::parseLayer(const std::string& id, const JSVal& value, util::pt it->second.second); stack.pop_front(); - util::ptr<StyleLayer> reference = it->second.second; + StyleLayer* reference = it->second.second.get(); if (!reference) { return; } @@ -283,4 +288,4 @@ void StyleParser::parseVisibility(StyleLayer& layer, const JSVal& value) { layer.visibility = VisibilityTypeClass({ value["visibility"].GetString(), value["visibility"].GetStringLength() }); } -} +} // namespace mbgl diff --git a/src/mbgl/style/style_parser.hpp b/src/mbgl/style/style_parser.hpp index 489c9959f25..2c5a43065da 100644 --- a/src/mbgl/style/style_parser.hpp +++ b/src/mbgl/style/style_parser.hpp @@ -1,8 +1,8 @@ #ifndef MBGL_STYLE_STYLE_PARSER #define MBGL_STYLE_STYLE_PARSER +#include <mbgl/style/style_layer.hpp> #include <mbgl/map/source.hpp> -#include <mbgl/util/ptr.hpp> #include <rapidjson/document.h> @@ -21,45 +21,29 @@ using JSVal = rapidjson::Value; class StyleParser { public: - void parse(const JSVal&); - - std::vector<std::unique_ptr<Source>>&& getSources() { - return std::move(sources); - } + ~StyleParser(); - std::vector<util::ptr<StyleLayer>> getLayers() { - return layers; - } + void parse(const JSVal&); - std::string getSpriteURL() const { - return spriteURL; - } + std::string spriteURL; + std::string glyphURL; - std::string getGlyphURL() const { - return glyphURL; - } + std::vector<std::unique_ptr<Source>> sources; + std::vector<std::unique_ptr<StyleLayer>> layers; private: void parseSources(const JSVal&); void parseLayers(const JSVal&); - void parseLayer(const std::string& id, const JSVal&, util::ptr<StyleLayer>&); + void parseLayer(const std::string& id, const JSVal&, std::unique_ptr<StyleLayer>&); void parseVisibility(StyleLayer&, const JSVal& value); - std::uint8_t version; - - std::vector<std::unique_ptr<Source>> sources; - std::vector<util::ptr<StyleLayer>> layers; - std::unordered_map<std::string, const Source*> sourcesMap; - std::unordered_map<std::string, std::pair<const JSVal&, util::ptr<StyleLayer>>> layersMap; + std::unordered_map<std::string, std::pair<const JSVal&, std::unique_ptr<StyleLayer>>> layersMap; // Store a stack of layer IDs we're parsing right now. This is to prevent reference cycles. std::forward_list<std::string> stack; - - std::string spriteURL; - std::string glyphURL; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/style_update_parameters.hpp b/src/mbgl/style/style_update_parameters.hpp new file mode 100644 index 00000000000..7a0f34bf56f --- /dev/null +++ b/src/mbgl/style/style_update_parameters.hpp @@ -0,0 +1,50 @@ +#ifndef STYLE_UPDATE_PARAMETERS +#define STYLE_UPDATE_PARAMETERS + +#include <mbgl/map/mode.hpp> + +namespace mbgl { + +class TransformState; +class Worker; +class TexturePool; +class MapData; +class Style; + +class StyleUpdateParameters { +public: + StyleUpdateParameters(float pixelRatio_, + MapDebugOptions debugOptions_, + TimePoint animationTime_, + const TransformState& transformState_, + Worker& worker_, + TexturePool& texturePool_, + bool shouldReparsePartialTiles_, + MapData& data_, + Style& style_) + : pixelRatio(pixelRatio_), + debugOptions(debugOptions_), + animationTime(animationTime_), + transformState(transformState_), + worker(worker_), + texturePool(texturePool_), + shouldReparsePartialTiles(shouldReparsePartialTiles_), + data(data_), + style(style_) {} + + float pixelRatio; + MapDebugOptions debugOptions; + TimePoint animationTime; + const TransformState& transformState; + Worker& worker; + TexturePool& texturePool; + bool shouldReparsePartialTiles; + + // TODO: remove + MapData& data; + Style& style; +}; + +} + +#endif diff --git a/src/mbgl/style/value.hpp b/src/mbgl/style/value.hpp index 4649134b204..0f3127e4f98 100644 --- a/src/mbgl/style/value.hpp +++ b/src/mbgl/style/value.hpp @@ -23,7 +23,7 @@ inline bool parseNumericString(const std::string &str, double &result) { while (*end != '\0' && isspace(*end)) end++; // eat whitespace after the end return errno == 0 && end - begin == long(str.size()); } -} +} // namespace util template <typename T> T toNumber(const Value &value) { @@ -38,6 +38,6 @@ T toNumber(const Value &value) { else return 0; } -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/value_comparison.hpp b/src/mbgl/style/value_comparison.hpp index 895c8a0869b..9f6ae3a0dec 100644 --- a/src/mbgl/style/value_comparison.hpp +++ b/src/mbgl/style/value_comparison.hpp @@ -102,8 +102,8 @@ inline bool relaxed_less_equal(Value const &lhs, Value const &rhs) { return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_less_equal_operator>(), lhs, rhs); } -} +} // namespace util -} +} // namespace mbgl #endif diff --git a/src/mbgl/style/zoom_history.hpp b/src/mbgl/style/zoom_history.hpp index 9eb76e5ec14..4e01af1fdd5 100644 --- a/src/mbgl/style/zoom_history.hpp +++ b/src/mbgl/style/zoom_history.hpp @@ -34,6 +34,6 @@ struct ZoomHistory { lastZoom = z; } }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/text/check_max_angle.cpp b/src/mbgl/text/check_max_angle.cpp index 73e7dd26be4..6e94c2ec175 100644 --- a/src/mbgl/text/check_max_angle.cpp +++ b/src/mbgl/text/check_max_angle.cpp @@ -75,4 +75,4 @@ bool checkMaxAngle(const std::vector<Coordinate> &line, Anchor &anchor, const fl } -} +} // namespace mbgl diff --git a/src/mbgl/text/check_max_angle.hpp b/src/mbgl/text/check_max_angle.hpp index 5a881ebbad3..1354abe95e2 100644 --- a/src/mbgl/text/check_max_angle.hpp +++ b/src/mbgl/text/check_max_angle.hpp @@ -9,6 +9,6 @@ namespace mbgl { bool checkMaxAngle(const std::vector<Coordinate> &line, Anchor &anchor, const float labelLength, const float windowSize, const float maxAngle); -} +} // namespace mbgl #endif diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp index 2a61e0b10e1..a84e75c85a2 100644 --- a/src/mbgl/text/collision_feature.cpp +++ b/src/mbgl/text/collision_feature.cpp @@ -92,4 +92,4 @@ void CollisionFeature::bboxifyLabel(const std::vector<Coordinate> &line, } -} +} // namespace mbgl diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp index 06056c2a20e..f112e17d85d 100644 --- a/src/mbgl/text/collision_feature.hpp +++ b/src/mbgl/text/collision_feature.hpp @@ -57,6 +57,6 @@ namespace mbgl { private: void bboxifyLabel(const std::vector<Coordinate> &line, const Anchor &anchor, const float length, const float height); }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp index ecd615a5209..dbd9a6cb10c 100644 --- a/src/mbgl/text/collision_tile.cpp +++ b/src/mbgl/text/collision_tile.cpp @@ -101,4 +101,4 @@ Box CollisionTile::getTreeBox(const vec2<float> &anchor, const CollisionBox &box }; } -} +} // namespace mbgl diff --git a/src/mbgl/text/get_anchors.cpp b/src/mbgl/text/get_anchors.cpp index 6492fd06790..2f991d8e871 100644 --- a/src/mbgl/text/get_anchors.cpp +++ b/src/mbgl/text/get_anchors.cpp @@ -49,7 +49,7 @@ Anchors resample(const std::vector<Coordinate> &line, const float offset, const // This has the most effect for short lines in overscaled tiles, since the // initial offset used in overscaled tiles is calculated to align labels with positions in // parent tiles instead of placing the label as close to the beginning as possible. - anchors = std::move(resample(line, distance / 2, spacing, angleWindowSize, maxAngle, labelLength, continuedLine, true)); + anchors = resample(line, distance / 2, spacing, angleWindowSize, maxAngle, labelLength, continuedLine, true); } return anchors; @@ -93,4 +93,4 @@ Anchors getAnchors(const std::vector<Coordinate> &line, float spacing, return resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength * boxScale, continuedLine, false); } -} +} // namespace mbgl diff --git a/src/mbgl/text/get_anchors.hpp b/src/mbgl/text/get_anchors.hpp index 3f737ee6c72..fcb0578d5b5 100644 --- a/src/mbgl/text/get_anchors.hpp +++ b/src/mbgl/text/get_anchors.hpp @@ -10,6 +10,6 @@ Anchors getAnchors(const std::vector<Coordinate> &line, float spacing, const float maxAngle, const float textLeft, const float textRight, const float iconLeft, const float iconRight, const float glyphSize, const float boxScale, const float overscaling); -} +} // namespace mbgl #endif diff --git a/src/mbgl/text/glyph.cpp b/src/mbgl/text/glyph.cpp index 3dca03504cc..a877d7a7993 100644 --- a/src/mbgl/text/glyph.cpp +++ b/src/mbgl/text/glyph.cpp @@ -11,4 +11,4 @@ GlyphRange getGlyphRange(char32_t glyph) { return { start, end }; } -} +} // namespace mbgl diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp index d1ba71c767c..62949ee3fd1 100644 --- a/src/mbgl/text/glyph.hpp +++ b/src/mbgl/text/glyph.hpp @@ -4,6 +4,7 @@ #include <mbgl/util/rect.hpp> #include <cstdint> +#include <utility> #include <vector> #include <string> #include <map> @@ -59,7 +60,7 @@ class Shaping { public: inline explicit Shaping() : top(0), bottom(0), left(0), right(0) {} inline explicit Shaping(float x, float y, std::u32string text_) - : text(text_), top(y), bottom(y), left(x), right(x) {} + : text(std::move(text_)), top(y), bottom(y), left(x), right(x) {} std::vector<PositionedGlyph> positionedGlyphs; std::u32string text; int32_t top; diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp index 9d7deb238d7..c14f52de7a3 100644 --- a/src/mbgl/text/glyph_pbf.cpp +++ b/src/mbgl/text/glyph_pbf.cpp @@ -59,7 +59,7 @@ void parseGlyphPBF(mbgl::FontStack& stack, const std::string& data) { } } -} +} // namespace namespace mbgl { @@ -138,4 +138,4 @@ void GlyphPBF::emitGlyphPBFLoadingFailed(const std::string& message) { observer->onGlyphPBFLoadingFailed(error); } -} +} // namespace mbgl diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp index 69d7747471a..d595298516f 100644 --- a/src/mbgl/text/glyph_pbf.hpp +++ b/src/mbgl/text/glyph_pbf.hpp @@ -50,6 +50,6 @@ class GlyphPBF : private util::noncopyable { Observer* observer = nullptr; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp index eb770fbf6e8..047db17b7d0 100644 --- a/src/mbgl/text/glyph_store.cpp +++ b/src/mbgl/text/glyph_store.cpp @@ -3,6 +3,8 @@ #include <mbgl/text/glyph_pbf.hpp> #include <mbgl/util/thread_context.hpp> +#include <cassert> + namespace mbgl { void GlyphStore::requestGlyphRange(const std::string& fontStackName, const GlyphRange& range) { @@ -80,4 +82,4 @@ void GlyphStore::setObserver(Observer* observer_) { observer = observer_; } -} +} // namespace mbgl diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp index 1f569664f2f..4e665087ead 100644 --- a/src/mbgl/text/glyph_store.hpp +++ b/src/mbgl/text/glyph_store.hpp @@ -70,6 +70,6 @@ class GlyphStore : public GlyphPBF::Observer, private util::noncopyable { Observer* observer = nullptr; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index 379a6e71ee8..20427924a40 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -208,4 +208,4 @@ SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, return quads; } -} +} // namespace mbgl diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index 814452a72f8..cdf7c23627f 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -43,6 +43,6 @@ namespace mbgl { SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, const float boxScale, const std::vector<Coordinate>& line, const SymbolLayoutProperties& layout, const bool alongLine, const GlyphPositions& face); -} +} // namespace mbgl #endif diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index 6709a49aa2b..735841e8cae 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -14,4 +14,4 @@ PositionedIcon shapeIcon(const Rect<uint16_t>& image, const SymbolLayoutProperti return PositionedIcon(image, y1, y2, x1, x2); } -} +} // namespace mbgl diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index 32b34cf5be8..acf8c470bff 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -26,6 +26,6 @@ namespace mbgl { PositionedIcon shapeIcon(const Rect<uint16_t>& image, const SymbolLayoutProperties&); -} +} // namespace mbgl #endif diff --git a/src/mbgl/util/async_task.hpp b/src/mbgl/util/async_task.hpp new file mode 100644 index 00000000000..5159e6ce3c7 --- /dev/null +++ b/src/mbgl/util/async_task.hpp @@ -0,0 +1,28 @@ +#ifndef MBGL_UTIL_ASYNC_TASK +#define MBGL_UTIL_ASYNC_TASK + +#include <mbgl/util/noncopyable.hpp> + +#include <memory> +#include <functional> + +namespace mbgl { +namespace util { + +class AsyncTask : private util::noncopyable { +public: + AsyncTask(std::function<void()>&&); + ~AsyncTask(); + + void send(); + void unref(); + +private: + class Impl; + std::unique_ptr<Impl> impl; +}; + +} // namespace util +} // namespace mbgl + +#endif diff --git a/src/mbgl/util/box.hpp b/src/mbgl/util/box.hpp index f03adc2e502..2806fb95fc0 100644 --- a/src/mbgl/util/box.hpp +++ b/src/mbgl/util/box.hpp @@ -11,6 +11,6 @@ struct box { TileCoordinate tl, tr, br, bl; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/util/chrono.cpp b/src/mbgl/util/chrono.cpp new file mode 100644 index 00000000000..ca8ebca9539 --- /dev/null +++ b/src/mbgl/util/chrono.cpp @@ -0,0 +1,20 @@ +#include <mbgl/util/chrono.hpp> + +namespace mbgl { + +template Duration toDuration<Clock, Duration>(TimePoint); +template SystemDuration toDuration<SystemClock, SystemDuration>(SystemTimePoint); + +template Seconds asSeconds<Duration>(Duration); +template Seconds asSeconds<Milliseconds>(Milliseconds); + +template Seconds toSeconds<Clock, Duration>(TimePoint); +template Seconds toSeconds<SystemClock, SystemDuration>(SystemTimePoint); + +template Milliseconds asMilliseconds<Duration>(Duration); +template Milliseconds asMilliseconds<Seconds>(Seconds); + +template Milliseconds toMilliseconds<Clock, Duration>(TimePoint); +template Milliseconds toMilliseconds<SystemClock, SystemDuration>(SystemTimePoint); + +} // namespace mbgl diff --git a/src/mbgl/util/clip_id.cpp b/src/mbgl/util/clip_id.cpp index 901e978fbe0..b89315d97bf 100644 --- a/src/mbgl/util/clip_id.cpp +++ b/src/mbgl/util/clip_id.cpp @@ -94,4 +94,4 @@ void ClipIDGenerator::update(std::forward_list<Tile *> tiles) { } } -} +} // namespace mbgl diff --git a/src/mbgl/util/clip_id.hpp b/src/mbgl/util/clip_id.hpp index 3940e60524f..71a07708c96 100644 --- a/src/mbgl/util/clip_id.hpp +++ b/src/mbgl/util/clip_id.hpp @@ -51,6 +51,6 @@ class ClipIDGenerator { }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp index 2ca11e3ad55..5dc3f1127c4 100644 --- a/src/mbgl/util/compression.cpp +++ b/src/mbgl/util/compression.cpp @@ -96,5 +96,5 @@ std::string decompress(const std::string &raw) { return result; } -} -} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/compression.hpp b/src/mbgl/util/compression.hpp index a33b2476a7a..2e7a8bbc0c6 100644 --- a/src/mbgl/util/compression.hpp +++ b/src/mbgl/util/compression.hpp @@ -9,7 +9,7 @@ namespace util { std::string compress(const std::string &raw); std::string decompress(const std::string &raw); -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/default_styles.cpp b/src/mbgl/util/default_styles.cpp index d1fc199b64c..d051fc60c37 100644 --- a/src/mbgl/util/default_styles.cpp +++ b/src/mbgl/util/default_styles.cpp @@ -11,6 +11,6 @@ const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v8", " const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v8", "Satellite" }; const DefaultStyle hybrid = { "mapbox://styles/mapbox/satellite-hybrid-v8", "Hybrid" }; -} +} // namespace default_styles } // end namespace util } // end namespace mbgl diff --git a/src/mbgl/util/gl_object_store.cpp b/src/mbgl/util/gl_object_store.cpp index 28a36e66398..78d6700237d 100644 --- a/src/mbgl/util/gl_object_store.cpp +++ b/src/mbgl/util/gl_object_store.cpp @@ -4,6 +4,8 @@ #include <mbgl/geometry/vao.hpp> #include <mbgl/platform/gl.hpp> +#include <cassert> + namespace mbgl { namespace util { @@ -44,5 +46,5 @@ void GLObjectStore::performCleanup() { } } -} -} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/gl_object_store.hpp b/src/mbgl/util/gl_object_store.hpp index d2929b5182f..775d4d1d012 100644 --- a/src/mbgl/util/gl_object_store.hpp +++ b/src/mbgl/util/gl_object_store.hpp @@ -29,7 +29,7 @@ class GLObjectStore : private util::noncopyable { std::vector<GLuint> abandonedTextures; }; -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/interpolate.hpp b/src/mbgl/util/interpolate.hpp index 28bbde8bf9f..03d401c3503 100644 --- a/src/mbgl/util/interpolate.hpp +++ b/src/mbgl/util/interpolate.hpp @@ -49,7 +49,7 @@ template<> inline RotationAlignmentType interpolate(const RotationAlignmentType template<> inline Faded<std::string> interpolate(const Faded<std::string>, const Faded<std::string> b, const double) { return b; } template<> inline Faded<std::vector<float>> interpolate(const Faded<std::vector<float>>, const Faded<std::vector<float>> b, const double) { return b; } -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp index 0f7ce0e55de..f37ce09cffd 100644 --- a/src/mbgl/util/io.cpp +++ b/src/mbgl/util/io.cpp @@ -39,5 +39,5 @@ void deleteFile(const std::string& filename) { } } -} -} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/io.hpp b/src/mbgl/util/io.hpp index bf15253ee46..1bcfe56af63 100644 --- a/src/mbgl/util/io.hpp +++ b/src/mbgl/util/io.hpp @@ -18,7 +18,7 @@ std::string read_file(const std::string &filename); void deleteFile(const std::string& filename); -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/mapbox.hpp b/src/mbgl/util/mapbox.hpp index 57f87e79596..29ee4548944 100644 --- a/src/mbgl/util/mapbox.hpp +++ b/src/mbgl/util/mapbox.hpp @@ -20,8 +20,8 @@ std::string normalizeTileURL(const std::string& url, const std::string& sourceUR // sometimes have multiple valid URLs. std::string canonicalURL(const std::string &url); -} -} -} +} // namespace mapbox +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/mat2.hpp b/src/mbgl/util/mat2.hpp index b5cec821f9a..c89088950d3 100644 --- a/src/mbgl/util/mat2.hpp +++ b/src/mbgl/util/mat2.hpp @@ -35,7 +35,7 @@ void identity(mat2& out); void rotate(mat2& out, const mat2& a, double rad); void scale(mat2& out, const mat2& a, double v0, double v1); -} -} +} // namespace matrix +} // namespace mbgl #endif diff --git a/src/mbgl/util/mat3.hpp b/src/mbgl/util/mat3.hpp index 3a6aba5a118..b0ecb17467f 100644 --- a/src/mbgl/util/mat3.hpp +++ b/src/mbgl/util/mat3.hpp @@ -36,7 +36,7 @@ void translate(mat3& out, const mat3& a, double x, double y); void rotate(mat3& out, const mat3& a, double rad); void scale(mat3& out, const mat3& a, double x, double y); -} -} +} // namespace matrix +} // namespace mbgl #endif diff --git a/src/mbgl/util/math.cpp b/src/mbgl/util/math.cpp index a7eab2d7718..a5247541092 100644 --- a/src/mbgl/util/math.cpp +++ b/src/mbgl/util/math.cpp @@ -21,5 +21,5 @@ uint32_t ceil_log2(uint64_t x) { return y; } -} -} \ No newline at end of file +} // namespace util +} // namespace mbgl \ No newline at end of file diff --git a/src/mbgl/util/premultiply.cpp b/src/mbgl/util/premultiply.cpp index 8a62012251c..e0178fda33e 100644 --- a/src/mbgl/util/premultiply.cpp +++ b/src/mbgl/util/premultiply.cpp @@ -23,7 +23,7 @@ PremultipliedImage premultiply(UnassociatedImage&& src) { b = (b * a + 127) / 255; } - return std::move(dst); + return dst; } UnassociatedImage unpremultiply(PremultipliedImage&& src) { @@ -46,8 +46,8 @@ UnassociatedImage unpremultiply(PremultipliedImage&& src) { } } - return std::move(dst); + return dst; } -} -} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/premultiply.hpp b/src/mbgl/util/premultiply.hpp index 8d87c157c8c..f64f3f4555a 100644 --- a/src/mbgl/util/premultiply.hpp +++ b/src/mbgl/util/premultiply.hpp @@ -9,7 +9,7 @@ namespace util { PremultipliedImage premultiply(UnassociatedImage&&); UnassociatedImage unpremultiply(PremultipliedImage&&); -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp index c4d65ff3201..a4c1d911348 100644 --- a/src/mbgl/util/raster.cpp +++ b/src/mbgl/util/raster.cpp @@ -3,7 +3,6 @@ #include <mbgl/platform/log.hpp> #include <mbgl/util/raster.hpp> -#include <mbgl/util/uv_detail.hpp> #include <cassert> #include <cstring> diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp index 5c65c46e9d6..618c1a18a56 100644 --- a/src/mbgl/util/raster.hpp +++ b/src/mbgl/util/raster.hpp @@ -9,8 +9,6 @@ #include <mutex> -typedef struct uv_loop_s uv_loop_t; - namespace mbgl { class Raster : public std::enable_shared_from_this<Raster> { @@ -61,6 +59,6 @@ class Raster : public std::enable_shared_from_this<Raster> { PremultipliedImage img; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/util/rect.hpp b/src/mbgl/util/rect.hpp index f4eb0679c41..7bb3214b9bc 100644 --- a/src/mbgl/util/rect.hpp +++ b/src/mbgl/util/rect.hpp @@ -5,7 +5,7 @@ namespace mbgl { template <typename T> struct Rect { - inline Rect() {} + inline Rect() = default; inline Rect(T x_, T y_, T w_, T h_) : x(x_), y(y_), w(w_), h(h_) {} T x = 0, y = 0; T w = 0, h = 0; @@ -23,6 +23,6 @@ struct Rect { inline bool hasArea() const { return w != 0 && h != 0; } }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/util/run_loop.cpp b/src/mbgl/util/run_loop.cpp deleted file mode 100644 index 7f277c98858..00000000000 --- a/src/mbgl/util/run_loop.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include <mbgl/util/run_loop.hpp> - -namespace mbgl { -namespace util { - -uv::tls<RunLoop> RunLoop::current; - -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) { - std::lock_guard<std::mutex> lock(mutex); - fn(); -} - -void RunLoop::process() { - Queue queue_; - withMutex([&] { queue_.swap(queue); }); - - while (!queue_.empty()) { - (*(queue_.front()))(); - queue_.pop(); - } -} - -void RunLoop::stop() { - invoke([&] { async.unref(); }); -} - -} -} diff --git a/src/mbgl/util/scaling.cpp b/src/mbgl/util/scaling.cpp index efc2709df38..9b32650511a 100644 --- a/src/mbgl/util/scaling.cpp +++ b/src/mbgl/util/scaling.cpp @@ -42,7 +42,7 @@ vec2<uint32_t> getBounds(const vec2<uint32_t>& srcSize, const Rect<uint32_t>& sr std::min(uint32_t(double(srcSize.y - srcPos.y) / factor.y), std::min(dstSize.y - dstPos.y, dstPos.h)) }; } -} +} // namespace namespace mbgl { namespace util { @@ -108,5 +108,5 @@ void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize } } -} -} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/scaling.hpp b/src/mbgl/util/scaling.hpp index 74a43eb6211..a2d582de1c0 100644 --- a/src/mbgl/util/scaling.hpp +++ b/src/mbgl/util/scaling.hpp @@ -17,7 +17,7 @@ void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos); -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/stopwatch.hpp b/src/mbgl/util/stopwatch.hpp index 57807afa767..a79ad92617b 100644 --- a/src/mbgl/util/stopwatch.hpp +++ b/src/mbgl/util/stopwatch.hpp @@ -35,7 +35,7 @@ class stopwatch { inline ~stopwatch() {} }; #endif -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/texture_pool.hpp b/src/mbgl/util/texture_pool.hpp index 95d918c237f..a980584c13c 100644 --- a/src/mbgl/util/texture_pool.hpp +++ b/src/mbgl/util/texture_pool.hpp @@ -20,6 +20,6 @@ class TexturePool : private util::noncopyable { std::set<GLuint> texture_ids; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp index d7611254ccc..a7a5e737821 100644 --- a/src/mbgl/util/thread.hpp +++ b/src/mbgl/util/thread.hpp @@ -112,28 +112,21 @@ Thread<Object>::Thread(const ThreadContext& context, Args&&... args) { template <class Object> template <typename P, std::size_t... I> void Thread<Object>::run(ThreadContext context, P&& params, std::index_sequence<I...>) { - uv::loop l; + ThreadContext::Set(&context); - ThreadContext::current.set(&context); + RunLoop loop_(RunLoop::Type::New); + loop = &loop_; - { - RunLoop loop_(l.get()); - loop = &loop_; + Object object_(std::get<I>(std::forward<P>(params))...); + object = &object_; - Object object_(std::get<I>(std::forward<P>(params))...); - object = &object_; + running.set_value(); + loop_.run(); - running.set_value(); - l.run(); + loop = nullptr; + object = nullptr; - loop = nullptr; - object = nullptr; - } - - // Run the loop again to ensure that async close callbacks have been called. - l.run(); - - ThreadContext::current.set(nullptr); + ThreadContext::Set(nullptr); joinable.get_future().get(); } @@ -145,7 +138,7 @@ Thread<Object>::~Thread() { thread.join(); } -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/thread_context.cpp b/src/mbgl/util/thread_context.cpp index 6742d5e75ec..b611b3d0230 100644 --- a/src/mbgl/util/thread_context.cpp +++ b/src/mbgl/util/thread_context.cpp @@ -1,34 +1,99 @@ #include <mbgl/util/thread_context.hpp> +#include <mbgl/util/thread_local.hpp> + +#include <cassert> + +namespace { + +using namespace mbgl::util; +static ThreadLocal<ThreadContext>& current = *new ThreadLocal<ThreadContext>; + +} // namespace + namespace mbgl { namespace util { +ThreadContext::ThreadContext(const std::string& name_, ThreadType type_, ThreadPriority priority_) + : name(name_), + type(type_), + priority(priority_) { +} + +void ThreadContext::Set(ThreadContext* context) { + current.set(context); +} + +bool ThreadContext::currentlyOn(ThreadType type) { + return current.get()->type == type; +} + +std::string ThreadContext::getName() { + if (current.get() != nullptr) { + return current.get()->name; + } else { + return "Unknown"; + } +} + +ThreadPriority ThreadContext::getPriority() { + if (current.get() != nullptr) { + return current.get()->priority; + } else { + return ThreadPriority::Regular; + } +} + +FileSource* ThreadContext::getFileSource() { + if (current.get() != nullptr) { + return current.get()->fileSource; + } else { + return nullptr; + } +} + +void ThreadContext::setFileSource(FileSource* fileSource) { + if (current.get() != nullptr) { + current.get()->fileSource = fileSource; + } else { + throw std::runtime_error("Current thread has no current ThreadContext."); + } +} + +GLObjectStore* ThreadContext::getGLObjectStore() { + if (current.get() != nullptr) { + return current.get()->glObjectStore; + } else { + return nullptr; + } +} + +void ThreadContext::setGLObjectStore(GLObjectStore* glObjectStore) { + if (current.get() != nullptr) { + current.get()->glObjectStore = glObjectStore; + } else { + throw std::runtime_error("Current thread has no current ThreadContext."); + } +} + class MainThreadContextRegistrar { public: MainThreadContextRegistrar() : context("Main", ThreadType::Main, ThreadPriority::Regular) { - ThreadContext::current.set(&context); + ThreadContext::Set(&context); } ~MainThreadContextRegistrar() { - ThreadContext::current.set(nullptr); + ThreadContext::Set(nullptr); } private: ThreadContext context; }; -ThreadContext::ThreadContext(const std::string& name_, ThreadType type_, ThreadPriority priority_) - : name(name_), - type(type_), - priority(priority_) { -} - -uv::tls<ThreadContext> ThreadContext::current; - // Will auto register the main thread context // at startup. Must be instantiated after the // ThreadContext::current object. MainThreadContextRegistrar registrar; -} -} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/thread_context.hpp b/src/mbgl/util/thread_context.hpp index 626925bdcdc..2b22b2faf46 100644 --- a/src/mbgl/util/thread_context.hpp +++ b/src/mbgl/util/thread_context.hpp @@ -1,8 +1,6 @@ #ifndef MBGL_UTIL_THREAD_CONTEXT #define MBGL_UTIL_THREAD_CONTEXT -#include <mbgl/util/uv_detail.hpp> - #include <cstdint> #include <string> #include <thread> @@ -31,73 +29,26 @@ struct ThreadContext { public: ThreadContext(const std::string& name, ThreadType type, ThreadPriority priority); - static bool currentlyOn(ThreadType type) { - return current.get()->type == type; - } - - static std::string getName() { - if (current.get() != nullptr) { - return current.get()->name; - } else { - return "Unknown"; - } - } - - static ThreadPriority getPriority() { - if (current.get() != nullptr) { - return current.get()->priority; - } else { - return ThreadPriority::Regular; - } - } - - static FileSource* getFileSource() { - if (current.get() != nullptr) { - return current.get()->fileSource; - } else { - return nullptr; - } - } + static void Set(ThreadContext* context); - static void setFileSource(FileSource* fileSource) { - if (current.get() != nullptr) { - current.get()->fileSource = fileSource; - } else { - throw new std::runtime_error("Current thread has no current ThreadContext."); - } - } + static bool currentlyOn(ThreadType type); + static std::string getName(); + static ThreadPriority getPriority(); - static GLObjectStore* getGLObjectStore() { - if (current.get() != nullptr) { - return current.get()->glObjectStore; - } else { - return nullptr; - } - } + static FileSource* getFileSource(); + static void setFileSource(FileSource* fileSource); + static GLObjectStore* getGLObjectStore(); + static void setGLObjectStore(GLObjectStore* glObjectStore); - static void setGLObjectStore(GLObjectStore* glObjectStore) { - if (current.get() != nullptr) { - current.get()->glObjectStore = glObjectStore; - } else { - throw new std::runtime_error("Current thread has no current ThreadContext."); - } - } - -private: std::string name; ThreadType type; ThreadPriority priority; FileSource* fileSource = nullptr; GLObjectStore* glObjectStore = nullptr; - - static uv::tls<ThreadContext> current; - - friend class MainThreadContextRegistrar; - template <class Object> friend class Thread; }; -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/thread_local.hpp b/src/mbgl/util/thread_local.hpp new file mode 100644 index 00000000000..54050373589 --- /dev/null +++ b/src/mbgl/util/thread_local.hpp @@ -0,0 +1,59 @@ +#ifndef MBGL_UTIL_THREAD_LOCAL +#define MBGL_UTIL_THREAD_LOCAL + +#include <mbgl/util/noncopyable.hpp> + +#include <stdexcept> + +#include <pthread.h> + +namespace mbgl { +namespace util { + +template <class T> +class ThreadLocal : public noncopyable { +public: + inline ThreadLocal(T* val) { + ThreadLocal(); + set(val); + } + + inline ThreadLocal() { + int ret = pthread_key_create(&key, [](void *ptr) { + delete reinterpret_cast<T *>(ptr); + }); + + if (ret) { + throw std::runtime_error("Failed to init local storage key."); + } + } + + inline ~ThreadLocal() { + if (pthread_key_delete(key)) { + throw std::runtime_error("Failed to delete local storage key."); + } + } + + inline T* get() { + T* ret = reinterpret_cast<T*>(pthread_getspecific(key)); + if (!ret) { + return nullptr; + } + + return ret; + } + + inline void set(T* ptr) { + if (pthread_setspecific(key, ptr)) { + throw std::runtime_error("Failed to set local storage."); + } + } + +private: + pthread_key_t key; +}; + +} // namespace util +} // namespace mbgl + +#endif diff --git a/src/mbgl/util/tile_coordinate.hpp b/src/mbgl/util/tile_coordinate.hpp index 836539b5bba..4710e08d0ae 100644 --- a/src/mbgl/util/tile_coordinate.hpp +++ b/src/mbgl/util/tile_coordinate.hpp @@ -24,6 +24,6 @@ struct TileCoordinate { }; }; -} +} // namespace mbgl #endif diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index b7937bf849c..803aa53eeea 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -97,4 +97,4 @@ std::forward_list<TileID> tileCover(int8_t z, const mbgl::box &bounds, int8_t ac return t; } -} +} // namespace mbgl diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp index 99c19a3052f..0514c36b62c 100644 --- a/src/mbgl/util/tile_cover.hpp +++ b/src/mbgl/util/tile_cover.hpp @@ -10,6 +10,6 @@ namespace mbgl { std::forward_list<TileID> tileCover(int8_t z, const box& bounds, int8_t actualZ); -} +} // namespace mbgl #endif diff --git a/src/mbgl/util/time.cpp b/src/mbgl/util/time.cpp index 4612ae29b5e..575ed47e1ad 100644 --- a/src/mbgl/util/time.cpp +++ b/src/mbgl/util/time.cpp @@ -1,5 +1,4 @@ #include <mbgl/util/time.hpp> -#include <mbgl/util/uv_detail.hpp> #include <cstdio> @@ -19,5 +18,13 @@ std::string rfc1123(std::time_t time) { return buffer; } +std::string iso8601(std::time_t time) { + std::tm info; + gmtime_r(&time, &info); + char buffer[30]; + std::strftime(buffer, sizeof(buffer), "%F %T", &info); + return buffer; } -} + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/timer.hpp b/src/mbgl/util/timer.hpp new file mode 100644 index 00000000000..44a516d1684 --- /dev/null +++ b/src/mbgl/util/timer.hpp @@ -0,0 +1,30 @@ +#ifndef MBGL_UTIL_TIMER +#define MBGL_UTIL_TIMER + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/chrono.hpp> + +#include <memory> +#include <functional> + +namespace mbgl { +namespace util { + +class Timer : private util::noncopyable { +public: + Timer(); + ~Timer(); + + void start(Duration timeout, Duration repeat, std::function<void()>&&); + void stop(); + void unref(); + +private: + class Impl; + std::unique_ptr<Impl> impl; +}; + +} // namespace util +} // namespace mbgl + +#endif diff --git a/src/mbgl/util/transition.cpp b/src/mbgl/util/transition.cpp index bab5e12fddb..16e13108d2e 100644 --- a/src/mbgl/util/transition.cpp +++ b/src/mbgl/util/transition.cpp @@ -7,7 +7,7 @@ namespace mbgl { namespace util { UnitBezier ease(0, 0, 0.25, 1); -transition::~transition() {} +transition::~transition() = default; template <typename T> transition::state ease_transition<T>::update(const TimePoint& now) const { @@ -23,4 +23,5 @@ transition::state ease_transition<T>::update(const TimePoint& now) const { template class ease_transition<double>; -}} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/transition.hpp b/src/mbgl/util/transition.hpp index 5cfbc0a7cab..a947a9ad184 100644 --- a/src/mbgl/util/transition.hpp +++ b/src/mbgl/util/transition.hpp @@ -72,7 +72,7 @@ class timeout : public transition { T& value; }; -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/url.cpp b/src/mbgl/util/url.cpp index e9b96721099..bf6fc70ff58 100644 --- a/src/mbgl/util/url.cpp +++ b/src/mbgl/util/url.cpp @@ -47,5 +47,5 @@ std::string percentDecode(const std::string& input) { return decoded; } -} -} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/url.hpp b/src/mbgl/util/url.hpp index a7e5291ec51..6257a171c0c 100644 --- a/src/mbgl/util/url.hpp +++ b/src/mbgl/util/url.hpp @@ -9,7 +9,7 @@ namespace util { std::string percentEncode(const std::string&); std::string percentDecode(const std::string&); -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/uv.cpp b/src/mbgl/util/uv.cpp deleted file mode 100644 index c8181e0e36c..00000000000 --- a/src/mbgl/util/uv.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include <mbgl/util/uv.hpp> -#include <mbgl/util/uv_detail.hpp> -#include <mbgl/util/string.hpp> - -#include <uv.h> - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" -// 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; -#pragma GCC diagnostic pop - -#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 || (major == 0 && minor != UV_VERSION_MINOR)) { - 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) { - return -pthread_key_create(key, NULL); -} - -void uv_key_delete(uv_key_t* key) { - if (pthread_key_delete(*key)) - abort(); -} - -void* uv_key_get(uv_key_t* key) { - return pthread_getspecific(*key); -} - -void uv_key_set(uv_key_t* key, void* value) { - if (pthread_setspecific(*key, value)) - abort(); -} - -#endif - -namespace uv { - -lock::lock(mutex &mtx_) : mtx(&mtx_) { - if (mtx) { mtx->lock(); } -} -lock::lock(const std::unique_ptr<mutex> &mtx_) : mtx(mtx_.get()) { - if (mtx) { mtx->lock(); } -} -lock::~lock() { - if (mtx) { mtx->unlock(); } -} -lock::lock(lock &&other) { - std::swap(mtx, other.mtx); -} -lock &lock::operator=(lock &&other) { - std::swap(mtx, other.mtx); - return *this; -} - - -readlock::readlock(rwlock &mtx_) : mtx(&mtx_) { - if (mtx) { mtx->rdlock(); } -} -readlock::readlock(const std::unique_ptr<rwlock> &mtx_) : mtx(mtx_.get()) { - if (mtx) { mtx->rdlock(); } -} -readlock::~readlock() { - if (mtx) { mtx->rdunlock(); } -} -readlock::readlock(readlock &&lock) { - std::swap(mtx, lock.mtx); -} -readlock &readlock::operator=(readlock &&lock) { - std::swap(mtx, lock.mtx); - return *this; -} - - -writelock::writelock(rwlock &mtx_) : mtx(&mtx_) { - if (mtx) { mtx->wrlock(); } -} -writelock::writelock(const std::unique_ptr<rwlock> &mtx_) : mtx(mtx_.get()) { - if (mtx) { mtx->wrlock(); } -} -writelock::~writelock() { - if (mtx) { mtx->wrunlock(); } -} -writelock::writelock(writelock &&lock) { - std::swap(mtx, lock.mtx); -} -writelock &writelock::operator=(writelock &&lock) { - std::swap(mtx, lock.mtx); - return *this; -} - -const char *getFileRequestError(uv_fs_t *req) { -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - return uv_strerror(uv_last_error(req->loop)); -#else - return uv_strerror(int(req->result)); -#endif -} - -} diff --git a/src/mbgl/util/vec4.hpp b/src/mbgl/util/vec4.hpp index c5b4ab1d9e9..8f54f222612 100644 --- a/src/mbgl/util/vec4.hpp +++ b/src/mbgl/util/vec4.hpp @@ -34,7 +34,7 @@ typedef std::array<double, 4> vec4; void transformMat4(vec4& out, vec4& a, mat4& m); -} -} +} // namespace matrix +} // namespace mbgl #endif diff --git a/src/mbgl/util/version_info.cpp b/src/mbgl/util/version_info.cpp index 1d649d2b69f..c545ddcc409 100644 --- a/src/mbgl/util/version_info.cpp +++ b/src/mbgl/util/version_info.cpp @@ -10,5 +10,5 @@ const char *revision = MBGL_VERSION_REV; const char *string = MBGL_VERSION_STRING; const unsigned int number = MBGL_VERSION; -} -} \ No newline at end of file +} // namespace version +} // namespace mbgl \ No newline at end of file diff --git a/src/mbgl/util/work_queue.cpp b/src/mbgl/util/work_queue.cpp index 7e1406bba06..80c4af8778d 100644 --- a/src/mbgl/util/work_queue.cpp +++ b/src/mbgl/util/work_queue.cpp @@ -1,7 +1,8 @@ #include <mbgl/util/work_queue.hpp> - #include <mbgl/util/run_loop.hpp> +#include <cassert> + namespace mbgl { namespace util { @@ -33,5 +34,5 @@ void WorkQueue::pop(const std::function<void()>& fn) { queue.pop(); } -} -} +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/work_queue.hpp b/src/mbgl/util/work_queue.hpp index 55b687a4689..6694352918d 100644 --- a/src/mbgl/util/work_queue.hpp +++ b/src/mbgl/util/work_queue.hpp @@ -37,7 +37,7 @@ class WorkQueue : private util::noncopyable { RunLoop* runLoop; }; -} -} +} // namespace util +} // namespace mbgl #endif diff --git a/src/mbgl/util/work_request.cpp b/src/mbgl/util/work_request.cpp index 69e9397288d..eb84df7bf2c 100644 --- a/src/mbgl/util/work_request.cpp +++ b/src/mbgl/util/work_request.cpp @@ -2,10 +2,11 @@ #include <mbgl/util/work_task.hpp> #include <cassert> +#include <utility> namespace mbgl { -WorkRequest::WorkRequest(Task task_) : task(task_) { +WorkRequest::WorkRequest(Task task_) : task(std::move(task_)) { assert(task); } @@ -13,4 +14,4 @@ WorkRequest::~WorkRequest() { task->cancel(); } -} +} // namespace mbgl diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp index 00bdb5baeea..054f1df242c 100644 --- a/src/mbgl/util/worker.cpp +++ b/src/mbgl/util/worker.cpp @@ -4,6 +4,7 @@ #include <mbgl/platform/platform.hpp> #include <mbgl/renderer/raster_bucket.hpp> #include <mbgl/map/geometry_tile.hpp> +#include <mbgl/style/style_layer.hpp> #include <cassert> #include <future> @@ -33,12 +34,12 @@ class Worker::Impl { } void parseGeometryTile(TileWorker* worker, - std::vector<util::ptr<StyleLayer>> layers, + std::vector<std::unique_ptr<StyleLayer>> layers, std::unique_ptr<GeometryTile> tile, PlacementConfig config, std::function<void(TileParseResult)> callback) { try { - callback(worker->parseAllLayers(layers, *tile, config)); + callback(worker->parseAllLayers(std::move(layers), *tile, config)); } catch (const std::exception& ex) { callback(TileParseResult(ex.what())); } @@ -54,11 +55,11 @@ class Worker::Impl { } void redoPlacement(TileWorker* worker, - std::vector<util::ptr<StyleLayer>> layers, + std::vector<std::unique_ptr<StyleLayer>> layers, const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets, PlacementConfig config, std::function<void()> callback) { - worker->redoPlacement(layers, buckets, config); + worker->redoPlacement(std::move(layers), buckets, config); callback(); } }; @@ -83,7 +84,7 @@ Worker::parseRasterTile(std::unique_ptr<RasterBucket> bucket, std::unique_ptr<WorkRequest> Worker::parseGeometryTile(TileWorker& worker, - std::vector<util::ptr<StyleLayer>> layers, + std::vector<std::unique_ptr<StyleLayer>> layers, std::unique_ptr<GeometryTile> tile, PlacementConfig config, std::function<void(TileParseResult)> callback) { @@ -102,13 +103,13 @@ Worker::parsePendingGeometryTileLayers(TileWorker& worker, std::unique_ptr<WorkRequest> Worker::redoPlacement(TileWorker& worker, - std::vector<util::ptr<StyleLayer>> layers, + std::vector<std::unique_ptr<StyleLayer>> layers, const std::unordered_map<std::string, std::unique_ptr<Bucket>>& buckets, PlacementConfig config, std::function<void()> callback) { current = (current + 1) % threads.size(); return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker, - layers, &buckets, config); + std::move(layers), &buckets, config); } } // end namespace mbgl diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp index f0f291e24cd..31e45f12409 100644 --- a/src/mbgl/util/worker.hpp +++ b/src/mbgl/util/worker.hpp @@ -40,7 +40,7 @@ class Worker : public mbgl::util::noncopyable { std::function<void(RasterTileParseResult)> callback); Request parseGeometryTile(TileWorker&, - std::vector<util::ptr<StyleLayer>>, + std::vector<std::unique_ptr<StyleLayer>>, std::unique_ptr<GeometryTile>, PlacementConfig, std::function<void(TileParseResult)> callback); @@ -49,7 +49,7 @@ class Worker : public mbgl::util::noncopyable { std::function<void(TileParseResult)> callback); Request redoPlacement(TileWorker&, - std::vector<util::ptr<StyleLayer>>, + std::vector<std::unique_ptr<StyleLayer>>, const std::unordered_map<std::string, std::unique_ptr<Bucket>>&, PlacementConfig config, std::function<void()> callback); @@ -59,6 +59,6 @@ class Worker : public mbgl::util::noncopyable { std::vector<std::unique_ptr<util::Thread<Impl>>> threads; std::size_t current = 0; }; -} +} // namespace mbgl #endif diff --git a/test/api/annotations.cpp b/test/api/annotations.cpp index ae9c8047301..d3299443f8d 100644 --- a/test/api/annotations.cpp +++ b/test/api/annotations.cpp @@ -14,12 +14,29 @@ using namespace mbgl; +namespace { + +std::string getFileSourceRoot() { +#ifdef MBGL_ASSET_ZIP + return "test/fixtures/annotations/assets.zip"; +#else + return ""; +#endif +} + +} // namespace + +std::shared_ptr<SpriteImage> defaultMarker() { + PremultipliedImage image = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); + return std::make_shared<SpriteImage>(image.width, image.height, 1.0, std::string(reinterpret_cast<char*>(image.data.get()), image.size())); +} + PremultipliedImage render(Map& map) { std::promise<PremultipliedImage> promise; map.renderStill([&](std::exception_ptr, PremultipliedImage&& image) { promise.set_value(std::move(image)); }); - return std::move(promise.get_future().get()); + return promise.get_future().get(); } void checkRendering(Map& map, const char * name) { @@ -31,11 +48,12 @@ void checkRendering(Map& map, const char * name) { TEST(Annotations, PointAnnotation) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); - map.addPointAnnotation(PointAnnotation({ 0, -20 }, "default_marker")); + map.addAnnotationIcon("default_marker", defaultMarker()); + map.addPointAnnotation(PointAnnotation({ 0, 0 }, "default_marker")); checkRendering(map, "point_annotation"); } @@ -43,7 +61,7 @@ TEST(Annotations, PointAnnotation) { TEST(Annotations, LineAnnotation) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); @@ -62,7 +80,7 @@ TEST(Annotations, LineAnnotation) { TEST(Annotations, FillAnnotation) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); @@ -80,7 +98,7 @@ TEST(Annotations, FillAnnotation) { TEST(Annotations, StyleSourcedShapeAnnotation) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/annotation.json"), ""); @@ -95,15 +113,16 @@ TEST(Annotations, StyleSourcedShapeAnnotation) { TEST(Annotations, AddMultiple) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); - map.addPointAnnotation(PointAnnotation({ 0, -20 }, "default_marker")); + map.addAnnotationIcon("default_marker", defaultMarker()); + map.addPointAnnotation(PointAnnotation({ 0, -10 }, "default_marker")); render(map); - map.addPointAnnotation(PointAnnotation({ 0, 20 }, "default_marker")); + map.addPointAnnotation(PointAnnotation({ 0, 10 }, "default_marker")); checkRendering(map, "add_multiple"); } @@ -111,7 +130,7 @@ TEST(Annotations, AddMultiple) { TEST(Annotations, NonImmediateAdd) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); @@ -131,10 +150,11 @@ TEST(Annotations, NonImmediateAdd) { TEST(Annotations, RemovePoint) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); + map.addAnnotationIcon("default_marker", defaultMarker()); uint32_t point = map.addPointAnnotation(PointAnnotation({ 0, 0 }, "default_marker")); render(map); @@ -147,7 +167,7 @@ TEST(Annotations, RemovePoint) { TEST(Annotations, RemoveShape) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); AnnotationSegments segments = {{ {{ { 0, 0 }, { 45, 45 } }} }}; @@ -169,7 +189,7 @@ TEST(Annotations, RemoveShape) { TEST(Annotations, ImmediateRemoveShape) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.removeAnnotation(map.addShapeAnnotation(ShapeAnnotation({}, {}))); @@ -181,11 +201,12 @@ TEST(Annotations, ImmediateRemoveShape) { TEST(Annotations, SwitchStyle) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); + DefaultFileSource fileSource(nullptr, getFileSourceRoot()); Map map(view, fileSource, MapMode::Still); map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); - map.addPointAnnotation(PointAnnotation({ 0, -20 }, "default_marker")); + map.addAnnotationIcon("default_marker", defaultMarker()); + map.addPointAnnotation(PointAnnotation({ 0, 0 }, "default_marker")); render(map); @@ -193,16 +214,3 @@ TEST(Annotations, SwitchStyle) { checkRendering(map, "switch_style"); } - -TEST(Annotations, CustomIcon) { - auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display, 1); - DefaultFileSource fileSource(nullptr); - - Map map(view, fileSource, MapMode::Still); - map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); - map.setSprite("cafe", std::make_shared<SpriteImage>(12, 12, 1, std::string(12 * 12 * 4, '\xFF'))); - map.addPointAnnotation(PointAnnotation({ 0, 0 }, "cafe")); - - checkRendering(map, "custom_icon"); -} diff --git a/test/api/repeated_render.cpp b/test/api/repeated_render.cpp index 77e4569dbfb..d3d3926a493 100644 --- a/test/api/repeated_render.cpp +++ b/test/api/repeated_render.cpp @@ -17,7 +17,11 @@ TEST(API, RepeatedRender) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1, 256, 512); +#ifdef MBGL_ASSET_ZIP + DefaultFileSource fileSource(nullptr, "test/fixtures/api/assets.zip"); +#else DefaultFileSource fileSource(nullptr); +#endif Log::setObserver(std::make_unique<FixtureLogObserver>()); @@ -29,7 +33,7 @@ TEST(API, RepeatedRender) { map.renderStill([&promise](std::exception_ptr, PremultipliedImage&& image) { promise.set_value(std::move(image)); }); - auto result = std::move(promise.get_future().get()); + auto result = promise.get_future().get(); ASSERT_EQ(256, result.width); ASSERT_EQ(512, result.height); util::write_file("test/fixtures/api/1.png", encodePNG(result)); @@ -41,7 +45,7 @@ TEST(API, RepeatedRender) { map.renderStill([&promise](std::exception_ptr, PremultipliedImage&& image) { promise.set_value(std::move(image)); }); - auto result = std::move(promise.get_future().get()); + auto result = promise.get_future().get(); ASSERT_EQ(256, result.width); ASSERT_EQ(512, result.height); util::write_file("test/fixtures/api/2.png", encodePNG(result)); diff --git a/test/fixtures/annotations/add_multiple/expected.png b/test/fixtures/annotations/add_multiple/expected.png index deedf84330c..f77ddb0792f 100644 Binary files a/test/fixtures/annotations/add_multiple/expected.png and b/test/fixtures/annotations/add_multiple/expected.png differ diff --git a/test/fixtures/annotations/assets.zip b/test/fixtures/annotations/assets.zip new file mode 100644 index 00000000000..869380034f3 Binary files /dev/null and b/test/fixtures/annotations/assets.zip differ diff --git a/test/fixtures/annotations/fill_annotation/expected.png b/test/fixtures/annotations/fill_annotation/expected.png index 30f6f1eb592..09f48081a80 100644 Binary files a/test/fixtures/annotations/fill_annotation/expected.png and b/test/fixtures/annotations/fill_annotation/expected.png differ diff --git a/test/fixtures/annotations/line_annotation/expected.png b/test/fixtures/annotations/line_annotation/expected.png index 067027f1d17..b86570f9903 100644 Binary files a/test/fixtures/annotations/line_annotation/expected.png and b/test/fixtures/annotations/line_annotation/expected.png differ diff --git a/test/fixtures/annotations/non_immediate_add/expected.png b/test/fixtures/annotations/non_immediate_add/expected.png index 30f6f1eb592..09f48081a80 100644 Binary files a/test/fixtures/annotations/non_immediate_add/expected.png and b/test/fixtures/annotations/non_immediate_add/expected.png differ diff --git a/test/fixtures/annotations/point_annotation/expected.png b/test/fixtures/annotations/point_annotation/expected.png index 33299a2d6a4..21d38b20731 100644 Binary files a/test/fixtures/annotations/point_annotation/expected.png and b/test/fixtures/annotations/point_annotation/expected.png differ diff --git a/test/fixtures/annotations/switch_style/expected.png b/test/fixtures/annotations/switch_style/expected.png index 33299a2d6a4..21d38b20731 100644 Binary files a/test/fixtures/annotations/switch_style/expected.png and b/test/fixtures/annotations/switch_style/expected.png differ diff --git a/test/fixtures/api/assets.zip b/test/fixtures/api/assets.zip new file mode 100644 index 00000000000..42a30f1c2ae Binary files /dev/null and b/test/fixtures/api/assets.zip differ diff --git a/test/fixtures/api/empty.json b/test/fixtures/api/empty.json index acc8a630cab..61a8fadcdb0 100644 --- a/test/fixtures/api/empty.json +++ b/test/fixtures/api/empty.json @@ -1,6 +1,5 @@ { "version": 8, "sources": {}, - "layers": [], - "sprite": "asset://TEST_DATA/fixtures/resources/sprite" + "layers": [] } diff --git a/test/fixtures/mock_file_source.cpp b/test/fixtures/mock_file_source.cpp index ea580276d6d..b0d70188739 100644 --- a/test/fixtures/mock_file_source.cpp +++ b/test/fixtures/mock_file_source.cpp @@ -3,16 +3,11 @@ #include <mbgl/util/io.hpp> #include <mbgl/util/thread.hpp> +#include <mbgl/util/timer.hpp> #include <algorithm> #include <unordered_map> -namespace { - -const uint64_t timeout = 1000000; - -} - namespace mbgl { class MockFileRequest : public FileRequest { @@ -36,8 +31,8 @@ class MockFileRequest : public FileRequest { class MockFileSource::Impl { public: Impl(Type type, const std::string& match) - : type_(type), match_(match), timer_(util::RunLoop::getLoop()) { - timer_.start(timeout, timeout, [this] { dispatchPendingRequests(); }); + : type_(type), match_(match) { + timer_.start(std::chrono::milliseconds(1000), std::chrono::milliseconds(1000), [this] { dispatchPendingRequests(); }); timer_.unref(); } @@ -63,7 +58,7 @@ class MockFileSource::Impl { Type type_; std::string match_; std::unordered_map<FileRequest*, std::pair<Resource, Callback>> pendingRequests_; - uv::timer timer_; + util::Timer timer_; std::function<void(void)> requestEnqueuedCallback_; }; @@ -72,7 +67,7 @@ void MockFileSource::Impl::replyWithSuccess(Resource resource, Callback callback Response res; try { - res.data = std::make_shared<const std::string>(std::move(util::read_file(resource.url))); + res.data = std::make_shared<const std::string>(util::read_file(resource.url)); } catch (const std::exception& err) { res.error = std::make_unique<Response::Error>(Response::Error::Reason::Other, err.what()); } @@ -108,7 +103,7 @@ void MockFileSource::Impl::replyWithCorruptedData(Resource resource, Callback ca } Response res; - auto data = std::make_shared<std::string>(std::move(util::read_file(resource.url))); + auto data = std::make_shared<std::string>(util::read_file(resource.url)); data->insert(0, "CORRUPTED"); res.data = std::move(data); callback(res); @@ -163,4 +158,4 @@ void MockFileSource::cancel(FileRequest* req) { thread_->invoke(&Impl::cancelRequest, req); } -} +} // namespace mbgl diff --git a/test/fixtures/sprites/default_marker.png b/test/fixtures/sprites/default_marker.png new file mode 100644 index 00000000000..b112096c180 Binary files /dev/null and b/test/fixtures/sprites/default_marker.png differ diff --git a/test/fixtures/util.cpp b/test/fixtures/util.cpp index ebd530eaacd..c3ce1b7efb7 100644 --- a/test/fixtures/util.cpp +++ b/test/fixtures/util.cpp @@ -113,5 +113,5 @@ void checkImage(const std::string& base, util::write_file(base + "/diff.png", encodePNG(diff)); } -} -} +} // namespace test +} // namespace mbgl diff --git a/test/miscellaneous/async_task.cpp b/test/miscellaneous/async_task.cpp new file mode 100644 index 00000000000..c1a2bd82e05 --- /dev/null +++ b/test/miscellaneous/async_task.cpp @@ -0,0 +1,129 @@ +#include <mbgl/util/async_task.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread.hpp> + +#include "../fixtures/util.hpp" + +#include <vector> + +using namespace mbgl::util; + +namespace { + +class TestWorker { +public: + TestWorker(AsyncTask *async_) + : async(async_) {} + + void run() { + for (unsigned i = 0; i < 100000; ++i) { + async->send(); + } + } + + void runWithCallback(std::function<void()> cb) { + for (unsigned i = 0; i < 100000; ++i) { + async->send(); + } + + cb(); + } + +private: + AsyncTask *async; +}; + +} // namespace + +TEST(AsyncTask, RequestCoalescing) { + RunLoop loop; + + unsigned count = 0; + AsyncTask async([&count] { ++count; }); + async.unref(); + + async.send(); + async.send(); + async.send(); + async.send(); + async.send(); + + loop.runOnce(); + + EXPECT_EQ(count, 1); +} + +TEST(AsyncTask, DestroyShouldNotRunQueue) { + RunLoop loop; + + unsigned count = 0; + auto async = std::make_unique<AsyncTask>([&count] { ++count; }); + + async->send(); + async.reset(); + + EXPECT_EQ(count, 0); +} + +TEST(AsyncTask, RequestCoalescingMultithreaded) { + RunLoop loop; + + unsigned count = 0; + AsyncTask async([&count] { ++count; }); + async.unref(); + + std::vector<std::unique_ptr<Thread<TestWorker>>> threads; + ThreadContext context = {"Test", ThreadType::Map, ThreadPriority::Regular}; + + unsigned numThreads = 50; + for (unsigned i = 0; i < numThreads; ++i) { + std::unique_ptr<Thread<TestWorker>> thread = + std::make_unique<Thread<TestWorker>>(context, &async); + + thread->invoke(&TestWorker::run); + threads.push_back(std::move(thread)); + } + + // Join all the threads + threads.clear(); + + loop.runOnce(); + + EXPECT_EQ(count, 1); +} + +TEST(AsyncTask, ThreadSafety) { + RunLoop loop; + + unsigned count = 0; + AsyncTask async([&count] { ++count; }); + async.unref(); + + unsigned numThreads = 50; + + auto callback = [&] { + if (!--numThreads) { + loop.stop(); + } + }; + + std::vector<std::unique_ptr<Thread<TestWorker>>> threads; + std::vector<std::unique_ptr<mbgl::WorkRequest>> requests; + ThreadContext context = {"Test", ThreadType::Map, ThreadPriority::Regular}; + + for (unsigned i = 0; i < numThreads; ++i) { + std::unique_ptr<Thread<TestWorker>> thread = + std::make_unique<Thread<TestWorker>>(context, &async); + + requests.push_back( + thread->invokeWithCallback(&TestWorker::runWithCallback, callback)); + + threads.push_back(std::move(thread)); + } + + loop.run(); + + // We expect here more than 1 but 1 would also be + // a valid result, although very unlikely (I hope). + EXPECT_GT(count, 1); +} diff --git a/test/miscellaneous/binpack.cpp b/test/miscellaneous/binpack.cpp index a597f0a2990..54f410a0bea 100644 --- a/test/miscellaneous/binpack.cpp +++ b/test/miscellaneous/binpack.cpp @@ -9,7 +9,7 @@ namespace mbgl { template <typename T> ::std::ostream& operator<<(::std::ostream& os, const Rect<T>& t) { return os << "Rect { " << t.x << ", " << t.y << ", " << t.w << ", " << t.h << " }"; } -} +} // namespace mbgl TEST(BinPack, Allocating) { mbgl::BinPack<uint16_t> bin(128, 128); diff --git a/test/miscellaneous/map_context.cpp b/test/miscellaneous/map_context.cpp index fac21ee9597..9645572358c 100644 --- a/test/miscellaneous/map_context.cpp +++ b/test/miscellaneous/map_context.cpp @@ -13,9 +13,9 @@ TEST(MapContext, DoubleStyleLoad) { std::shared_ptr<HeadlessDisplay> display = std::make_shared<HeadlessDisplay>(); HeadlessView view(display, 1, 512, 512); DefaultFileSource fileSource(nullptr); - MapData data(MapMode::Continuous, GLContextMode::Unique, view.getPixelRatio()); - util::Thread<MapContext> context({"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, view, fileSource, data); + util::Thread<MapContext> context({"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, + view, fileSource, MapMode::Continuous, GLContextMode::Unique, view.getPixelRatio()); context.invokeSync(&MapContext::setStyleJSON, "", ""); context.invokeSync(&MapContext::setStyleJSON, "", ""); diff --git a/test/miscellaneous/style_parser.cpp b/test/miscellaneous/style_parser.cpp index 7a38ba054d4..ed9a0462b50 100644 --- a/test/miscellaneous/style_parser.cpp +++ b/test/miscellaneous/style_parser.cpp @@ -20,7 +20,7 @@ typedef std::vector<Message> Messages; class StyleParserTest : public ::testing::TestWithParam<std::string> {}; TEST_P(StyleParserTest, ParseStyle) { - const std::string &base = "test/fixtures/style_parser/" + GetParam(); + const std::string base = std::string("test/fixtures/style_parser/") + GetParam(); rapidjson::Document infoDoc; infoDoc.Parse<0>(util::read_file(base + ".info.json").c_str()); diff --git a/test/miscellaneous/thread.cpp b/test/miscellaneous/thread.cpp index 9d8f6b6327f..f60d7d43dd9 100644 --- a/test/miscellaneous/thread.cpp +++ b/test/miscellaneous/thread.cpp @@ -66,7 +66,7 @@ class TestObject { TEST(Thread, invoke) { const std::thread::id tid = std::this_thread::get_id(); - RunLoop loop(uv_default_loop()); + RunLoop loop; std::vector<std::unique_ptr<mbgl::WorkRequest>> requests; loop.invoke([&] { @@ -110,7 +110,7 @@ TEST(Thread, invoke) { test.clear(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST(Thread, context) { @@ -122,7 +122,7 @@ TEST(Thread, context) { const std::thread::id tid = std::this_thread::get_id(); - RunLoop loop(uv_default_loop()); + RunLoop loop; std::vector<std::unique_ptr<mbgl::WorkRequest>> requests; loop.invoke([&] { @@ -134,7 +134,7 @@ TEST(Thread, context) { })); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } class TestWorker { @@ -148,7 +148,7 @@ class TestWorker { }; TEST(Thread, ExecutesAfter) { - RunLoop loop(uv_default_loop()); + RunLoop loop; Thread<TestWorker> thread({"Test", ThreadType::Map, ThreadPriority::Regular}); bool didWork = false; @@ -161,14 +161,15 @@ TEST(Thread, ExecutesAfter) { didWork = true; }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); EXPECT_TRUE(didWork); EXPECT_TRUE(didAfter); } TEST(Thread, WorkRequestDeletionWaitsForWorkToComplete) { - RunLoop loop(uv_default_loop()); + RunLoop loop; + Thread<TestWorker> thread({"Test", ThreadType::Map, ThreadPriority::Regular}); std::promise<void> started; @@ -186,7 +187,7 @@ TEST(Thread, WorkRequestDeletionWaitsForWorkToComplete) { } TEST(Thread, WorkRequestDeletionCancelsAfter) { - RunLoop loop(uv_default_loop()); + RunLoop loop; Thread<TestWorker> thread({"Test", ThreadType::Map, ThreadPriority::Regular}); std::promise<void> started; @@ -200,12 +201,12 @@ TEST(Thread, WorkRequestDeletionCancelsAfter) { started.get_future().get(); request.reset(); - uv_run(uv_default_loop(), UV_RUN_ONCE); + loop.runOnce(); EXPECT_FALSE(didAfter); } TEST(Thread, WorkRequestDeletionCancelsImmediately) { - RunLoop loop(uv_default_loop()); + RunLoop loop; Thread<TestWorker> thread({"Test", ThreadType::Map, ThreadPriority::Regular}); std::promise<void> started; diff --git a/test/miscellaneous/thread_local.cpp b/test/miscellaneous/thread_local.cpp new file mode 100644 index 00000000000..aeaf187540d --- /dev/null +++ b/test/miscellaneous/thread_local.cpp @@ -0,0 +1,95 @@ +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread.hpp> +#include <mbgl/util/thread_local.hpp> + +#include "../fixtures/util.hpp" + +using namespace mbgl::util; + +namespace { + +class TestThread { +public: + TestThread(int *number_) { + number.set(number_); + } + + ~TestThread() { + number.set(nullptr); + } + + int getNumber() { + return *number.get(); + } + +private: + static ThreadLocal<int> number; +}; + +ThreadLocal<int> TestThread::number; + +} // namespace + +TEST(ThreadLocalStorage, Basic) { + RunLoop loop; + + int number1 = 1; + int number2 = 2; + int number3 = 3; + + ThreadContext context = {"Test", ThreadType::Map, ThreadPriority::Regular}; + + Thread<TestThread> thread1(context, &number1); + Thread<TestThread> thread2(context, &number2); + Thread<TestThread> thread3(context, &number3); + + EXPECT_EQ(number1, thread1.invokeSync<int>(&TestThread::getNumber)); + EXPECT_EQ(number2, thread2.invokeSync<int>(&TestThread::getNumber)); + EXPECT_EQ(number3, thread3.invokeSync<int>(&TestThread::getNumber)); +} + +TEST(ThreadLocalStorage, NotSetReturnsNull) { + static ThreadLocal<int> number; + + EXPECT_EQ(nullptr, number.get()); +} + +namespace { + +struct DtorCounter { + ~DtorCounter() { ++(*value); } + unsigned *value; +}; + +class TestThreadReclaim { +public: + TestThreadReclaim(DtorCounter* counter_) { + counter.set(counter_); + } + +private: + static ThreadLocal<DtorCounter> counter; +}; + +ThreadLocal<DtorCounter> TestThreadReclaim::counter; + +} // namespace + +TEST(ThreadLocalStorage, AutoReclaim) { + RunLoop loop; + + unsigned counter = 0; + + DtorCounter* dtorCounter1 = new DtorCounter{ &counter }; + DtorCounter* dtorCounter2 = new DtorCounter{ &counter }; + + ThreadContext context = {"Test", ThreadType::Map, ThreadPriority::Regular}; + + auto thread1 = std::make_unique<Thread<TestThreadReclaim>>(context, dtorCounter1); + auto thread2 = std::make_unique<Thread<TestThreadReclaim>>(context, dtorCounter2); + + thread1.reset(); + thread2.reset(); + + EXPECT_EQ(counter, 2); +} diff --git a/test/miscellaneous/timer.cpp b/test/miscellaneous/timer.cpp new file mode 100644 index 00000000000..aaac4e2f8e7 --- /dev/null +++ b/test/miscellaneous/timer.cpp @@ -0,0 +1,179 @@ +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/timer.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <memory> + +#include "../fixtures/util.hpp" + +using namespace mbgl::util; + +TEST(Timer, Basic) { + RunLoop loop; + + Timer timer; + timer.unref(); + + auto callback = [&loop] { loop.stop(); }; + + auto interval = std::chrono::milliseconds(300); + auto expectedTotalTime = interval; + + auto first = mbgl::Clock::now(); + timer.start(interval, mbgl::Duration::zero(), callback); + + loop.run(); + + using namespace std::chrono; + auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); + + // These are not high precision timers. Especially libuv uses + // cached time from the beginning of of the main loop iteration + // and it is very prone to fire earlier, which is, odd. + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, Repeat) { + RunLoop loop; + + Timer timer; + timer.unref(); + + unsigned count = 10; + auto callback = [&] { + if (!--count) { + loop.stop(); + } + }; + + auto interval = std::chrono::milliseconds(50); + auto expectedTotalTime = interval * count; + + auto first = mbgl::Clock::now(); + timer.start(interval, interval, callback); + + loop.run(); + + using namespace std::chrono; + auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); + + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, Stop) { + RunLoop loop; + + Timer timer1; + timer1.unref(); + + Timer timer2; + timer2.unref(); + + auto interval1 = std::chrono::milliseconds(50); + auto interval2 = std::chrono::milliseconds(250); + auto expectedTotalTime = interval2; + + int count = 0; + + auto callback1 = [&] { + ++count; + timer1.stop(); + }; + + auto callback2 = [&] { + ++count; + loop.stop(); + }; + + auto first = mbgl::Clock::now(); + timer1.start(interval1, interval1, callback1); + timer2.start(interval2, mbgl::Duration::zero(), callback2); + + loop.run(); + + using namespace std::chrono; + auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); + + EXPECT_EQ(count, 2); + + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, DestroyShouldStop) { + RunLoop loop; + + auto timer1 = std::make_unique<Timer>(); + timer1->unref(); + + Timer timer2; + timer2.unref(); + + auto interval1 = std::chrono::milliseconds(50); + auto interval2 = std::chrono::milliseconds(250); + auto expectedTotalTime = interval2; + + int count = 0; + + auto callback1 = [&] { + ++count; + timer1.reset(); + }; + + auto callback2 = [&] { + ++count; + loop.stop(); + }; + + auto first = mbgl::Clock::now(); + timer1->start(interval1, interval1, callback1); + timer2.start(interval2, mbgl::Duration::zero(), callback2); + + loop.run(); + + using namespace std::chrono; + auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); + + EXPECT_EQ(count, 2); + + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, StartOverrides) { + RunLoop loop; + + Timer timer; + timer.unref(); + + auto interval1 = std::chrono::milliseconds(50); + auto interval2 = std::chrono::milliseconds(250); + auto expectedTotalTime = interval1 + interval2; + + int count = 0; + + auto callback2 = [&] { + ++count; + loop.stop(); + }; + + auto callback1 = [&] { + ++count; + timer.start(interval2, mbgl::Duration::zero(), callback2); + }; + + auto first = mbgl::Clock::now(); + timer.start(interval1, mbgl::Duration::zero(), callback1); + + loop.run(); + + using namespace std::chrono; + auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); + + EXPECT_EQ(count, 2); + + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} diff --git a/test/miscellaneous/work_queue.cpp b/test/miscellaneous/work_queue.cpp index a5f616fe5bd..a6cd6c3f88f 100644 --- a/test/miscellaneous/work_queue.cpp +++ b/test/miscellaneous/work_queue.cpp @@ -23,7 +23,7 @@ class TestThread { }; TEST(WorkQueue, push) { - RunLoop loop(uv_default_loop()); + RunLoop loop; WorkQueue queue; Thread<TestThread> thread({"Test", ThreadType::Map, ThreadPriority::Regular}, &queue); @@ -43,11 +43,11 @@ TEST(WorkQueue, push) { thread.invoke(&TestThread::send, endTest); thread.invoke(&TestThread::send, endTest); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST(WorkQueue, cancel) { - RunLoop loop(uv_default_loop()); + RunLoop loop; WorkQueue queue; diff --git a/test/sprite/sprite_atlas.cpp b/test/sprite/sprite_atlas.cpp index e54a88dabe2..fd78e971fcf 100644 --- a/test/sprite/sprite_atlas.cpp +++ b/test/sprite/sprite_atlas.cpp @@ -27,9 +27,9 @@ TEST(Sprite, SpriteAtlas) { EXPECT_EQ(112, atlas.getTextureHeight()); // Image hasn't been created yet. - EXPECT_TRUE(atlas.getData()); + EXPECT_FALSE(atlas.getData()); - auto metro = atlas.getImage("metro", false); + auto metro = *atlas.getImage("metro", false); EXPECT_EQ(0, metro.pos.x); EXPECT_EQ(0, metro.pos.y); EXPECT_EQ(20, metro.pos.w); @@ -42,7 +42,9 @@ TEST(Sprite, SpriteAtlas) { EXPECT_EQ(18, metro.texture->pixelHeight); EXPECT_EQ(1.0f, metro.texture->pixelRatio); - auto pos = atlas.getPosition("metro", false); + EXPECT_TRUE(atlas.getData()); + + auto pos = *atlas.getPosition("metro", false); EXPECT_DOUBLE_EQ(20, pos.size[0]); EXPECT_DOUBLE_EQ(20, pos.size[1]); EXPECT_DOUBLE_EQ(1.0f / 63, pos.tl[0]); @@ -50,16 +52,8 @@ TEST(Sprite, SpriteAtlas) { EXPECT_DOUBLE_EQ(21.0f / 63, pos.br[0]); EXPECT_DOUBLE_EQ(21.0f / 112, pos.br[1]); - auto missing = atlas.getImage("doesnotexist", false); - EXPECT_FALSE(missing.pos.hasArea()); - EXPECT_EQ(0, missing.pos.x); - EXPECT_EQ(0, missing.pos.y); - EXPECT_EQ(0, missing.pos.w); - EXPECT_EQ(0, missing.pos.h); - EXPECT_EQ(0, missing.pos.originalW); - EXPECT_EQ(0, missing.pos.originalH); - EXPECT_FALSE(missing.texture); + EXPECT_FALSE(missing); EXPECT_EQ(1u, log.count({ EventSeverity::Info, @@ -69,7 +63,7 @@ TEST(Sprite, SpriteAtlas) { })); // Different wrapping mode produces different image. - auto metro2 = atlas.getImage("metro", true); + auto metro2 = *atlas.getImage("metro", true); EXPECT_EQ(20, metro2.pos.x); EXPECT_EQ(0, metro2.pos.y); EXPECT_EQ(20, metro2.pos.w); @@ -101,7 +95,7 @@ TEST(Sprite, SpriteAtlasSize) { EXPECT_EQ(89, atlas.getTextureWidth()); EXPECT_EQ(157, atlas.getTextureHeight()); - auto metro = atlas.getImage("metro", false); + auto metro = *atlas.getImage("metro", false); EXPECT_EQ(0, metro.pos.x); EXPECT_EQ(0, metro.pos.y); EXPECT_EQ(20, metro.pos.w); @@ -135,7 +129,7 @@ TEST(Sprite, SpriteAtlasUpdates) { EXPECT_EQ(32, atlas.getTextureHeight()); store.setSprite("one", std::make_shared<SpriteImage>(16, 12, 1, std::string(16 * 12 * 4, '\x00'))); - auto one = atlas.getImage("one", false); + auto one = *atlas.getImage("one", false); EXPECT_EQ(0, one.pos.x); EXPECT_EQ(0, one.pos.y); EXPECT_EQ(20, one.pos.w); diff --git a/test/sprite/sprite_store.cpp b/test/sprite/sprite_store.cpp index 14ea6c957b3..574af51b814 100644 --- a/test/sprite/sprite_store.cpp +++ b/test/sprite/sprite_store.cpp @@ -4,8 +4,10 @@ #include <mbgl/sprite/sprite_store.hpp> +#include <mbgl/util/async_task.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/thread.hpp> +#include <utility> using namespace mbgl; @@ -155,7 +157,7 @@ struct SpriteParams { class SpriteThread : public SpriteStore::Observer { public: - SpriteThread(FileSource* fileSource, SpriteTestCallback callback) : callback_(callback) { + SpriteThread(FileSource* fileSource, SpriteTestCallback callback) : callback_(std::move(callback)) { util::ThreadContext::setFileSource(fileSource); } @@ -186,9 +188,9 @@ class SpriteThread : public SpriteStore::Observer { class SpriteTest : public testing::Test { protected: void runTest(const SpriteParams& params, FileSource* fileSource, SpriteTestCallback callback) { - util::RunLoop loop(uv_default_loop()); + util::RunLoop loop; - async_ = std::make_unique<uv::async>(loop.get(), [&] { loop.stop(); }); + async_ = std::make_unique<util::AsyncTask>([&] { loop.stop(); }); async_->unref(); const util::ThreadContext context = {"Map", util::ThreadType::Map, util::ThreadPriority::Regular}; @@ -196,7 +198,7 @@ class SpriteTest : public testing::Test { util::Thread<SpriteThread> tester(context, fileSource, callback); tester.invoke(&SpriteThread::loadSprite, params); - uv_run(loop.get(), UV_RUN_DEFAULT); + loop.run(); tester.invoke(&SpriteThread::unloadSprite); } @@ -206,7 +208,7 @@ class SpriteTest : public testing::Test { } private: - std::unique_ptr<uv::async> async_; + std::unique_ptr<util::AsyncTask> async_; }; TEST_F(SpriteTest, LoadingSuccess) { diff --git a/test/storage/cache_response.cpp b/test/storage/cache_response.cpp index d089659aeb4..8207460aa47 100644 --- a/test/storage/cache_response.cpp +++ b/test/storage/cache_response.cpp @@ -1,9 +1,8 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/sqlite_cache.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> TEST_F(Storage, CacheResponse) { @@ -11,9 +10,9 @@ TEST_F(Storage, CacheResponse) { using namespace mbgl; + util::RunLoop loop; SQLiteCache cache(":memory:"); DefaultFileSource fs(&cache); - util::RunLoop loop(uv_default_loop()); const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/cache" }; Response response; @@ -27,8 +26,8 @@ TEST_F(Storage, CacheResponse) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Response 1", *res.data); - EXPECT_LT(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_LT(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); response = res; @@ -49,5 +48,5 @@ TEST_F(Storage, CacheResponse) { }); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/cache_revalidate.cpp b/test/storage/cache_revalidate.cpp index 540a25acd63..ab7fcd5d249 100644 --- a/test/storage/cache_revalidate.cpp +++ b/test/storage/cache_revalidate.cpp @@ -1,9 +1,8 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/sqlite_cache.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> TEST_F(Storage, CacheRevalidateSame) { @@ -11,9 +10,9 @@ TEST_F(Storage, CacheRevalidateSame) { using namespace mbgl; + util::RunLoop loop; SQLiteCache cache(":memory:"); DefaultFileSource fs(&cache); - util::RunLoop loop(uv_default_loop()); const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" }; std::unique_ptr<FileRequest> req1; @@ -31,8 +30,8 @@ TEST_F(Storage, CacheRevalidateSame) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Response", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("snowfall", res.etag); req2 = fs.request(revalidateSame, [&, res](Response res2) { @@ -53,8 +52,8 @@ TEST_F(Storage, CacheRevalidateSame) { EXPECT_EQ(res.data, res2.data); EXPECT_EQ("Response", *res2.data); // We use this to indicate that a 304 reply came back. - EXPECT_LT(0, res2.expires); - EXPECT_EQ(0, res2.modified); + EXPECT_LT(Seconds::zero(), res2.expires); + EXPECT_EQ(Seconds::zero(), res2.modified); // We're not sending the ETag in the 304 reply, but it should still be there. EXPECT_EQ("snowfall", res2.etag); @@ -63,7 +62,7 @@ TEST_F(Storage, CacheRevalidateSame) { }); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, CacheRevalidateModified) { @@ -71,9 +70,9 @@ TEST_F(Storage, CacheRevalidateModified) { using namespace mbgl; + util::RunLoop loop; SQLiteCache cache(":memory:"); DefaultFileSource fs(&cache); - util::RunLoop loop(uv_default_loop()); const Resource revalidateModified{ Resource::Unknown, "http://127.0.0.1:3000/revalidate-modified" }; @@ -92,8 +91,8 @@ TEST_F(Storage, CacheRevalidateModified) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Response", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(1420070400, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(1420070400, res.modified.count()); EXPECT_EQ("", res.etag); req2 = fs.request(revalidateModified, [&, res](Response res2) { @@ -114,8 +113,8 @@ TEST_F(Storage, CacheRevalidateModified) { EXPECT_EQ("Response", *res2.data); EXPECT_EQ(res.data, res2.data); // We use this to indicate that a 304 reply came back. - EXPECT_LT(0, res2.expires); - EXPECT_EQ(1420070400, res2.modified); + EXPECT_LT(Seconds::zero(), res2.expires); + EXPECT_EQ(1420070400, res2.modified.count()); EXPECT_EQ("", res2.etag); loop.stop(); @@ -123,7 +122,7 @@ TEST_F(Storage, CacheRevalidateModified) { }); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, CacheRevalidateEtag) { @@ -131,9 +130,9 @@ TEST_F(Storage, CacheRevalidateEtag) { using namespace mbgl; + util::RunLoop loop; SQLiteCache cache(":memory:"); DefaultFileSource fs(&cache); - util::RunLoop loop(uv_default_loop()); const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" }; std::unique_ptr<FileRequest> req1; @@ -151,8 +150,8 @@ TEST_F(Storage, CacheRevalidateEtag) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Response 1", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("response-1", res.etag); req2 = fs.request(revalidateEtag, [&, res](Response res2) { @@ -172,8 +171,8 @@ TEST_F(Storage, CacheRevalidateEtag) { ASSERT_TRUE(res2.data.get()); EXPECT_NE(res.data, res2.data); EXPECT_EQ("Response 2", *res2.data); - EXPECT_EQ(0, res2.expires); - EXPECT_EQ(0, res2.modified); + EXPECT_EQ(Seconds::zero(), res2.expires); + EXPECT_EQ(Seconds::zero(), res2.modified); EXPECT_EQ("response-2", res2.etag); loop.stop(); @@ -181,5 +180,5 @@ TEST_F(Storage, CacheRevalidateEtag) { }); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/directory_reading.cpp b/test/storage/directory_reading.cpp index fab52315faf..5ef2983abf5 100644 --- a/test/storage/directory_reading.cpp +++ b/test/storage/directory_reading.cpp @@ -1,8 +1,7 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> TEST_F(Storage, AssetReadDirectory) { @@ -10,32 +9,32 @@ TEST_F(Storage, AssetReadDirectory) { using namespace mbgl; + util::RunLoop loop; + #ifdef MBGL_ASSET_ZIP DefaultFileSource fs(nullptr, "test/fixtures/storage/assets.zip"); #else DefaultFileSource fs(nullptr); #endif - util::RunLoop loop(uv_default_loop()); - std::unique_ptr<FileRequest> req = fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, [&](Response res) { req.reset(); ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); EXPECT_EQ(false, res.stale); ASSERT_FALSE(res.data.get()); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); #ifdef MBGL_ASSET_ZIP - EXPECT_EQ("No such file", res.error->message); + EXPECT_EQ("Could not stat file in zip archive", res.error->message); #elif MBGL_ASSET_FS - EXPECT_EQ("illegal operation on a directory", res.error->message); + EXPECT_EQ("Is a directory", res.error->message); #endif loop.stop(); ReadDirectory.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/file_reading.cpp b/test/storage/file_reading.cpp index 3fc30df5243..7565ad3d66f 100644 --- a/test/storage/file_reading.cpp +++ b/test/storage/file_reading.cpp @@ -1,23 +1,103 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/platform/platform.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread.hpp> + +namespace { + +std::string getFileSourceRoot() { +#ifdef MBGL_ASSET_ZIP + return "test/fixtures/storage/assets.zip"; +#else + return ""; +#endif +} + +class TestWorker { +public: + TestWorker(mbgl::DefaultFileSource* fs_) : fs(fs_) {} + + void run(std::function<void()> endCallback) { + const std::string asset("asset://TEST_DATA/fixtures/storage/nonempty"); + + requestCallback = [this, asset, endCallback](mbgl::Response res) { + EXPECT_EQ(nullptr, res.error); + ASSERT_TRUE(res.data.get()); + EXPECT_EQ("content is here\n", *res.data); + + if (res.stale) { + return; + } + + if (!--numRequests) { + endCallback(); + request.reset(); + } else { + request = fs->request({ mbgl::Resource::Unknown, asset }, requestCallback); + } + }; + + request = fs->request({ mbgl::Resource::Unknown, asset }, requestCallback); + } + +private: + unsigned numRequests = 1000; + + mbgl::DefaultFileSource* fs; + std::unique_ptr<mbgl::FileRequest> request; + + std::function<void(mbgl::Response)> requestCallback; +}; + +} // namespace + +TEST_F(Storage, AssetStress) { + SCOPED_TEST(AssetStress) + + using namespace mbgl; + + util::RunLoop loop; + + mbgl::DefaultFileSource fs(nullptr, getFileSourceRoot()); + + unsigned numThreads = 50; + + auto callback = [&] { + if (!--numThreads) { + loop.stop(); + } + }; + + std::vector<std::unique_ptr<util::Thread<TestWorker>>> threads; + std::vector<std::unique_ptr<mbgl::WorkRequest>> requests; + util::ThreadContext context = { "Test", util::ThreadType::Map, util::ThreadPriority::Regular }; + + for (unsigned i = 0; i < numThreads; ++i) { + std::unique_ptr<util::Thread<TestWorker>> thread = + std::make_unique<util::Thread<TestWorker>>(context, &fs); + + requests.push_back( + thread->invokeWithCallback(&TestWorker::run, callback)); + + threads.push_back(std::move(thread)); + } + + loop.run(); + + AssetStress.finish(); +} TEST_F(Storage, AssetEmptyFile) { SCOPED_TEST(EmptyFile) using namespace mbgl; -#ifdef MBGL_ASSET_ZIP - DefaultFileSource fs(nullptr, "test/fixtures/storage/assets.zip"); -#else - DefaultFileSource fs(nullptr); -#endif + util::RunLoop loop; - util::RunLoop loop(uv_default_loop()); + DefaultFileSource fs(nullptr, getFileSourceRoot()); std::unique_ptr<FileRequest> req = fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" }, [&](Response res) { req.reset(); @@ -25,14 +105,14 @@ TEST_F(Storage, AssetEmptyFile) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_LT(1420000000, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_LT(1420000000, res.modified.count()); EXPECT_NE("", res.etag); loop.stop(); EmptyFile.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, AssetNonEmptyFile) { @@ -40,13 +120,9 @@ TEST_F(Storage, AssetNonEmptyFile) { using namespace mbgl; -#ifdef MBGL_ASSET_ZIP - DefaultFileSource fs(nullptr, "test/fixtures/storage/assets.zip"); -#else - DefaultFileSource fs(nullptr); -#endif + util::RunLoop loop; - util::RunLoop loop(uv_default_loop()); + DefaultFileSource fs(nullptr, getFileSourceRoot()); std::unique_ptr<FileRequest> req = fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/nonempty" }, [&](Response res) { req.reset(); @@ -54,8 +130,8 @@ TEST_F(Storage, AssetNonEmptyFile) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("content is here\n", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_LT(1420000000, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_LT(1420000000, res.modified.count()); EXPECT_NE("", res.etag); ASSERT_TRUE(res.data.get()); EXPECT_EQ("content is here\n", *res.data); @@ -63,7 +139,7 @@ TEST_F(Storage, AssetNonEmptyFile) { NonEmptyFile.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, AssetNonExistentFile) { @@ -71,13 +147,9 @@ TEST_F(Storage, AssetNonExistentFile) { using namespace mbgl; -#ifdef MBGL_ASSET_ZIP - DefaultFileSource fs(nullptr, "test/fixtures/storage/assets.zip"); -#else - DefaultFileSource fs(nullptr); -#endif + util::RunLoop loop; - util::RunLoop loop(uv_default_loop()); + DefaultFileSource fs(nullptr, getFileSourceRoot()); std::unique_ptr<FileRequest> req = fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/does_not_exist" }, [&](Response res) { req.reset(); @@ -85,17 +157,17 @@ TEST_F(Storage, AssetNonExistentFile) { EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); EXPECT_EQ(false, res.stale); ASSERT_FALSE(res.data.get()); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); #ifdef MBGL_ASSET_ZIP - EXPECT_EQ("No such file", res.error->message); + EXPECT_EQ("Could not stat file in zip archive", res.error->message); #elif MBGL_ASSET_FS - EXPECT_EQ("no such file or directory", res.error->message); + EXPECT_EQ("No such file or directory", res.error->message); #endif loop.stop(); NonExistentFile.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/http_cancel.cpp b/test/storage/http_cancel.cpp index eabc8959021..442a159fe51 100644 --- a/test/storage/http_cancel.cpp +++ b/test/storage/http_cancel.cpp @@ -1,9 +1,8 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/network_status.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> #include <cmath> @@ -13,8 +12,8 @@ TEST_F(Storage, HTTPCancel) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, @@ -23,7 +22,7 @@ TEST_F(Storage, HTTPCancel) { req.reset(); HTTPCancel.finish(); - uv_run(uv_default_loop(), UV_RUN_ONCE); + loop.runOnce(); } TEST_F(Storage, HTTPCancelMultiple) { @@ -31,8 +30,8 @@ TEST_F(Storage, HTTPCancelMultiple) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" }; @@ -45,13 +44,13 @@ TEST_F(Storage, HTTPCancelMultiple) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTPCancelMultiple.finish(); }); req2.reset(); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/http_coalescing.cpp b/test/storage/http_coalescing.cpp index 38fc0151a30..a9edf008396 100644 --- a/test/storage/http_coalescing.cpp +++ b/test/storage/http_coalescing.cpp @@ -1,8 +1,7 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> TEST_F(Storage, HTTPCoalescing) { @@ -13,8 +12,8 @@ TEST_F(Storage, HTTPCoalescing) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); static const Response *reference = nullptr; @@ -31,8 +30,8 @@ TEST_F(Storage, HTTPCoalescing) { EXPECT_EQ(nullptr, res.error); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); if (counter >= total) { @@ -51,7 +50,7 @@ TEST_F(Storage, HTTPCoalescing) { }); } - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, HTTPMultiple) { @@ -59,8 +58,8 @@ TEST_F(Storage, HTTPMultiple) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test?expires=2147483647" }; std::unique_ptr<FileRequest> req1; @@ -70,8 +69,8 @@ TEST_F(Storage, HTTPMultiple) { EXPECT_EQ(nullptr, res.error); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_EQ(2147483647, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(2147483647, res.expires.count()); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); // Start a second request for the same resource after the first one has been completed. @@ -86,8 +85,8 @@ TEST_F(Storage, HTTPMultiple) { EXPECT_EQ(nullptr, res2.error); ASSERT_TRUE(res2.data.get()); EXPECT_EQ("Hello World!", *res2.data); - EXPECT_EQ(2147483647, res2.expires); - EXPECT_EQ(0, res2.modified); + EXPECT_EQ(2147483647, res2.expires.count()); + EXPECT_EQ(Seconds::zero(), res2.modified); EXPECT_EQ("", res2.etag); loop.stop(); @@ -95,7 +94,7 @@ TEST_F(Storage, HTTPMultiple) { }); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } // Tests that we get stale responses from previous requests when requesting the same thing again. @@ -104,8 +103,8 @@ TEST_F(Storage, HTTPStale) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); int updates = 0; int stale = 0; @@ -119,8 +118,8 @@ TEST_F(Storage, HTTPStale) { ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); EXPECT_EQ(false, res.stale); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); // Don't start the request twice in case this callback gets fired multiple times. @@ -135,8 +134,8 @@ TEST_F(Storage, HTTPStale) { EXPECT_EQ(nullptr, res2.error); ASSERT_TRUE(res2.data.get()); EXPECT_EQ("Hello World!", *res2.data); - EXPECT_EQ(0, res2.expires); - EXPECT_EQ(0, res2.modified); + EXPECT_EQ(Seconds::zero(), res2.expires); + EXPECT_EQ(Seconds::zero(), res2.modified); EXPECT_EQ("", res2.etag); if (res2.stale) { @@ -152,7 +151,7 @@ TEST_F(Storage, HTTPStale) { }); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); EXPECT_EQ(1, stale); EXPECT_EQ(1, updates); diff --git a/test/storage/http_error.cpp b/test/storage/http_error.cpp index 276793b77fc..1889d85e008 100644 --- a/test/storage/http_error.cpp +++ b/test/storage/http_error.cpp @@ -1,9 +1,8 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/network_status.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> #include <cmath> @@ -13,16 +12,16 @@ TEST_F(Storage, HTTPTemporaryError) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); - const auto start = uv_hrtime(); + const auto start = Clock::now(); std::unique_ptr<FileRequest> req1 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, [&](Response res) { static int counter = 0; switch (counter++) { case 0: { - const auto duration = double(uv_hrtime() - start) / 1e9; + const auto duration = std::chrono::duration<const double>(Clock::now() - start).count(); EXPECT_GT(0.2, duration) << "Initial error request took too long"; ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Server, res.error->reason); @@ -30,21 +29,21 @@ TEST_F(Storage, HTTPTemporaryError) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); } break; case 1: { req1.reset(); - const auto duration = double(uv_hrtime() - start) / 1e9; + const auto duration = std::chrono::duration<const double>(Clock::now() - start).count(); EXPECT_LT(0.99, duration) << "Backoff timer didn't wait 1 second"; EXPECT_GT(1.2, duration) << "Backoff timer fired too late"; EXPECT_EQ(nullptr, res.error); EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTPTemporaryError.finish(); @@ -52,7 +51,7 @@ TEST_F(Storage, HTTPTemporaryError) { } }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, HTTPConnectionError) { @@ -60,15 +59,15 @@ TEST_F(Storage, HTTPConnectionError) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); - const auto start = uv_hrtime(); + const auto start = Clock::now(); std::unique_ptr<FileRequest> req2 = fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, [&](Response res) { static int counter = 0; static int wait = 0; - const auto duration = double(uv_hrtime() - start) / 1e9; + const auto duration = std::chrono::duration<const double>(Clock::now() - start).count(); EXPECT_LT(wait - 0.01, duration) << "Backoff timer didn't wait 1 second"; EXPECT_GT(wait + 0.2, duration) << "Backoff timer fired too late"; ASSERT_NE(nullptr, res.error); @@ -86,8 +85,8 @@ TEST_F(Storage, HTTPConnectionError) { #endif EXPECT_EQ(false, res.stale); ASSERT_FALSE(res.data.get()); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); if (counter == 2) { @@ -99,5 +98,5 @@ TEST_F(Storage, HTTPConnectionError) { counter++; }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/http_header_parsing.cpp b/test/storage/http_header_parsing.cpp index 041b138b60e..9f48e995b51 100644 --- a/test/storage/http_header_parsing.cpp +++ b/test/storage/http_header_parsing.cpp @@ -1,7 +1,5 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> @@ -13,8 +11,8 @@ TEST_F(Storage, HTTPExpiresParsing) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); std::unique_ptr<FileRequest> req1 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, @@ -24,14 +22,14 @@ TEST_F(Storage, HTTPExpiresParsing) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_EQ(1420797926, res.expires); - EXPECT_EQ(1420794326, res.modified); + EXPECT_EQ(1420797926, res.expires.count()); + EXPECT_EQ(1420794326, res.modified.count()); EXPECT_EQ("foo", res.etag); loop.stop(); HTTPExpiresTest.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, HTTPCacheControlParsing) { @@ -39,11 +37,10 @@ TEST_F(Storage, HTTPCacheControlParsing) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); - int64_t now = std::chrono::duration_cast<std::chrono::seconds>( - SystemClock::now().time_since_epoch()).count(); + const Seconds now = toSeconds(SystemClock::now()); std::unique_ptr<FileRequest> req2 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, [&](Response res) { @@ -52,12 +49,12 @@ TEST_F(Storage, HTTPCacheControlParsing) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); 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"; - EXPECT_EQ(0, res.modified); + EXPECT_GT(2, std::abs(res.expires.count() - now.count() - 120)) << "Expiration date isn't about 120 seconds in the future"; + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTPCacheControlTest.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/http_issue_1369.cpp b/test/storage/http_issue_1369.cpp index 0fe0f5fb1b3..fff773de88e 100644 --- a/test/storage/http_issue_1369.cpp +++ b/test/storage/http_issue_1369.cpp @@ -1,9 +1,8 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/sqlite_cache.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> // Test for https://github.com/mapbox/mapbox-gl-native/issue/1369 @@ -22,9 +21,9 @@ TEST_F(Storage, HTTPIssue1369) { using namespace mbgl; + util::RunLoop loop; SQLiteCache cache; DefaultFileSource fs(&cache); - util::RunLoop loop(uv_default_loop()); const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" }; @@ -38,12 +37,12 @@ TEST_F(Storage, HTTPIssue1369) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTPIssue1369.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/http_load.cpp b/test/storage/http_load.cpp index 0a6b0ef6539..947b850db6f 100644 --- a/test/storage/http_load.cpp +++ b/test/storage/http_load.cpp @@ -1,8 +1,7 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> TEST_F(Storage, HTTPLoad) { @@ -10,8 +9,8 @@ TEST_F(Storage, HTTPLoad) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); const int concurrency = 50; const int max = 10000; @@ -29,8 +28,8 @@ TEST_F(Storage, HTTPLoad) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); if (number <= max) { @@ -47,5 +46,5 @@ TEST_F(Storage, HTTPLoad) { req(i); } - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/http_other_loop.cpp b/test/storage/http_other_loop.cpp index b432c39ac2e..d9c90397ad0 100644 --- a/test/storage/http_other_loop.cpp +++ b/test/storage/http_other_loop.cpp @@ -1,8 +1,7 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> TEST_F(Storage, HTTPOtherLoop) { @@ -11,8 +10,8 @@ TEST_F(Storage, HTTPOtherLoop) { using namespace mbgl; // This file source launches a separate thread to do the processing. + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); std::unique_ptr<FileRequest> req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, [&](Response res) { @@ -21,12 +20,12 @@ TEST_F(Storage, HTTPOtherLoop) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTPOtherLoop.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/http_reading.cpp b/test/storage/http_reading.cpp index b072a6eb539..3d7f9926e13 100644 --- a/test/storage/http_reading.cpp +++ b/test/storage/http_reading.cpp @@ -1,10 +1,10 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/util/exception.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread_context.hpp> #include <future> @@ -13,27 +13,25 @@ TEST_F(Storage, HTTPTest) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); - - const auto mainThread = uv_thread_self(); std::unique_ptr<FileRequest> req1 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, [&](Response res) { req1.reset(); - EXPECT_EQ(uv_thread_self(), mainThread); + EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main)); EXPECT_EQ(nullptr, res.error); EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTPTest.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, HTTP404) { @@ -41,29 +39,27 @@ TEST_F(Storage, HTTP404) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); - - const auto mainThread = uv_thread_self(); std::unique_ptr<FileRequest> req2 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, [&](Response res) { req2.reset(); - EXPECT_EQ(uv_thread_self(), mainThread); + EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main)); ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason); EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Cannot GET /doesnotexist\n", *res.data); EXPECT_EQ("HTTP status code 404", res.error->message); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTP404.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, HTTP500) { @@ -71,29 +67,27 @@ TEST_F(Storage, HTTP500) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); - - const auto mainThread = uv_thread_self(); std::unique_ptr<FileRequest> req3 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/permanent-error" }, [&](Response res) { req3.reset(); - EXPECT_EQ(uv_thread_self(), mainThread); + EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main)); ASSERT_NE(nullptr, res.error); EXPECT_EQ(Response::Error::Reason::Server, res.error->reason); EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Server Error!", *res.data); EXPECT_EQ("HTTP status code 500", res.error->message); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTP500.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } TEST_F(Storage, HTTPNoCallback) { @@ -101,8 +95,8 @@ TEST_F(Storage, HTTPNoCallback) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); try { fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, diff --git a/test/storage/http_retry_network_status.cpp b/test/storage/http_retry_network_status.cpp index 08e3be89ab6..41cbed34311 100644 --- a/test/storage/http_retry_network_status.cpp +++ b/test/storage/http_retry_network_status.cpp @@ -1,11 +1,10 @@ #include "storage.hpp" -#include <mbgl/util/uv_detail.hpp> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/network_status.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> - +#include <mbgl/util/timer.hpp> // Test for https://github.com/mapbox/mapbox-gl-native/issues/2123 // @@ -18,8 +17,8 @@ TEST_F(Storage, HTTPNetworkStatusChange) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/delayed" }; @@ -30,25 +29,25 @@ TEST_F(Storage, HTTPNetworkStatusChange) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Response", *res.data); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); loop.stop(); HTTPNetworkStatusChange.finish(); }); // After 50 milliseconds, we're going to trigger a NetworkStatus change. - uv::timer reachableTimer(uv_default_loop()); - reachableTimer.start(50, 0, [] () { + util::Timer reachableTimer; + reachableTimer.start(std::chrono::milliseconds(50), Duration::zero(), [] () { mbgl::NetworkStatus::Reachable(); }); // This timer will keep the loop alive to make sure we would be getting a response in caes the // network status change triggered another change (which it shouldn't). - uv::timer delayTimer(uv_default_loop()); - delayTimer.start(300, 0, [] () {}); + util::Timer delayTimer; + delayTimer.start(std::chrono::milliseconds(300), Duration::zero(), [] () {}); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } // Tests that a change in network status preempts requests that failed due to connection or @@ -58,15 +57,15 @@ TEST_F(Storage, HTTPNetworkStatusChangePreempt) { using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); - const auto start = uv_hrtime(); + const auto start = Clock::now(); const Resource resource{ Resource::Unknown, "http://127.0.0.1:3001/test" }; std::unique_ptr<FileRequest> req = fs.request(resource, [&](Response res) { static int counter = 0; - const auto duration = double(uv_hrtime() - start) / 1e9; + const auto duration = std::chrono::duration<const double>(Clock::now() - start).count(); if (counter == 0) { EXPECT_GT(0.2, duration) << "Response came in too late"; } else if (counter == 1) { @@ -90,8 +89,8 @@ TEST_F(Storage, HTTPNetworkStatusChangePreempt) { #endif EXPECT_EQ(false, res.stale); ASSERT_FALSE(res.data.get()); - EXPECT_EQ(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_EQ(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); if (counter++ == 1) { @@ -102,10 +101,10 @@ TEST_F(Storage, HTTPNetworkStatusChangePreempt) { }); // After 400 milliseconds, we're going to trigger a NetworkStatus change. - uv::timer reachableTimer(uv_default_loop()); - reachableTimer.start(400, 0, [] () { + util::Timer reachableTimer; + reachableTimer.start(std::chrono::milliseconds(400), Duration::zero(), [] () { mbgl::NetworkStatus::Reachable(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); } diff --git a/test/storage/http_timeout.cpp b/test/storage/http_timeout.cpp index 2c1a1f1f600..26ce0f5c3a7 100644 --- a/test/storage/http_timeout.cpp +++ b/test/storage/http_timeout.cpp @@ -1,19 +1,17 @@ #include "storage.hpp" -#include <uv.h> - #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/network_status.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> - TEST_F(Storage, HTTPTimeout) { SCOPED_TEST(HTTPTimeout) using namespace mbgl; + util::RunLoop loop; DefaultFileSource fs(nullptr); - util::RunLoop loop(uv_default_loop()); int counter = 0; @@ -24,8 +22,8 @@ TEST_F(Storage, HTTPTimeout) { EXPECT_EQ(false, res.stale); ASSERT_TRUE(res.data.get()); EXPECT_EQ("Hello World!", *res.data); - EXPECT_LT(0, res.expires); - EXPECT_EQ(0, res.modified); + EXPECT_LT(Seconds::zero(), res.expires); + EXPECT_EQ(Seconds::zero(), res.modified); EXPECT_EQ("", res.etag); if (counter == 4) { req.reset(); @@ -34,7 +32,7 @@ TEST_F(Storage, HTTPTimeout) { } }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + loop.run(); EXPECT_EQ(4, counter); } diff --git a/test/storage/storage.cpp b/test/storage/storage.cpp index ddc6fef5fbe..7fafe59e478 100644 --- a/test/storage/storage.cpp +++ b/test/storage/storage.cpp @@ -11,4 +11,4 @@ void Storage::SetUpTestCase() { void Storage::TearDownTestCase() { mbgl::test::stopServer(pid); -} \ No newline at end of file +} diff --git a/test/storage/storage.hpp b/test/storage/storage.hpp index 4c14746c6a1..34fa69fbf90 100644 --- a/test/storage/storage.hpp +++ b/test/storage/storage.hpp @@ -3,7 +3,6 @@ #include "../fixtures/util.hpp" #include <mbgl/storage/response.hpp> -#include <uv.h> #include <iostream> class Storage : public testing::Test { diff --git a/test/style/glyph_store.cpp b/test/style/glyph_store.cpp index fe614e8c60d..45247162122 100644 --- a/test/style/glyph_store.cpp +++ b/test/style/glyph_store.cpp @@ -4,8 +4,10 @@ #include <mbgl/text/font_stack.hpp> #include <mbgl/text/glyph_store.hpp> +#include <mbgl/util/async_task.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/thread.hpp> +#include <utility> using namespace mbgl; @@ -19,7 +21,7 @@ struct GlyphStoreParams { class GlyphStoreThread : public GlyphStore::Observer { public: - GlyphStoreThread(FileSource* fileSource, GlyphStoreTestCallback callback) : callback_(callback) { + GlyphStoreThread(FileSource* fileSource, GlyphStoreTestCallback callback) : callback_(std::move(callback)) { util::ThreadContext::setFileSource(fileSource); } @@ -53,9 +55,9 @@ class GlyphStoreThread : public GlyphStore::Observer { class GlyphStoreTest : public testing::Test { protected: void runTest(const GlyphStoreParams& params, FileSource* fileSource, GlyphStoreTestCallback callback) { - util::RunLoop loop(uv_default_loop()); + util::RunLoop loop; - async_ = std::make_unique<uv::async>(loop.get(), [&]{ loop.stop(); }); + async_ = std::make_unique<util::AsyncTask>([&]{ loop.stop(); }); async_->unref(); const util::ThreadContext context = {"Map", util::ThreadType::Map, util::ThreadPriority::Regular}; @@ -63,7 +65,7 @@ class GlyphStoreTest : public testing::Test { util::Thread<GlyphStoreThread> tester(context, fileSource, callback); tester.invoke(&GlyphStoreThread::loadGlyphStore, params); - uv_run(loop.get(), UV_RUN_DEFAULT); + loop.run(); tester.invoke(&GlyphStoreThread::unloadGlyphStore); } @@ -80,7 +82,7 @@ class GlyphStoreTest : public testing::Test { private: bool testDone = false; - std::unique_ptr<uv::async> async_; + std::unique_ptr<util::AsyncTask> async_; }; TEST_F(GlyphStoreTest, LoadingSuccess) { diff --git a/test/style/pending_resources.cpp b/test/style/pending_resources.cpp index 81452ffc7ea..7f665e85e27 100644 --- a/test/style/pending_resources.cpp +++ b/test/style/pending_resources.cpp @@ -5,9 +5,9 @@ #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_display.hpp> #include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/util/async_task.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> -#include <mbgl/util/uv_detail.hpp> using namespace mbgl; @@ -20,7 +20,7 @@ class PendingResources : public ::testing::TestWithParam<std::string> { // the Map object after that. The idea here is to test if these pending requests // are getting canceled correctly if on shutdown. TEST_P(PendingResources, DeleteMapObjectWithPendingRequest) { - util::RunLoop loop(uv_default_loop()); + util::RunLoop loop; auto display = std::make_shared<mbgl::HeadlessDisplay>(); HeadlessView view(display, 1, 1000, 1000); @@ -28,7 +28,7 @@ TEST_P(PendingResources, DeleteMapObjectWithPendingRequest) { std::unique_ptr<Map> map = std::make_unique<Map>(view, fileSource, MapMode::Still); - uv::async endTest(loop.get(), [&map, &loop] { + util::AsyncTask endTest([&map, &loop] { map.reset(); loop.stop(); }); @@ -43,7 +43,7 @@ TEST_P(PendingResources, DeleteMapObjectWithPendingRequest) { EXPECT_TRUE(false) << "Should never happen."; }); - uv_run(loop.get(), UV_RUN_DEFAULT); + loop.run(); } // In the test data below, "sprite" will match both "sprite.json" and "sprite.png" and cause two diff --git a/test/style/resource_loading.cpp b/test/style/resource_loading.cpp index cdc84e96b20..c646f0372aa 100644 --- a/test/style/resource_loading.cpp +++ b/test/style/resource_loading.cpp @@ -80,7 +80,7 @@ class MockMapContext : public Style::Observer { void runTestCase(MockFileSource::Type type, const std::string& param, const std::string& message) { - util::RunLoop loop(uv_default_loop()); + util::RunLoop loop; MockView view; MockFileSource fileSource(type, param); @@ -118,7 +118,7 @@ void runTestCase(MockFileSource::Type type, std::make_unique<util::Thread<MockMapContext>>( util::ThreadContext{"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, view, fileSource, callback)); - uv_run(loop.get(), UV_RUN_DEFAULT); + loop.run(); // Needed because it will make the Map thread // join and cease logging after this point. @@ -141,7 +141,7 @@ void runTestCase(MockFileSource::Type type, } } -} +} // namespace class ResourceLoading : public ::testing::TestWithParam<std::pair<std::string, std::string>> { }; diff --git a/test/test.gypi b/test/test.gypi index c96b4f686c4..708b89e74f8 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -45,6 +45,7 @@ 'api/set_style.cpp', + 'miscellaneous/async_task.cpp', 'miscellaneous/clip_ids.cpp', 'miscellaneous/binpack.cpp', 'miscellaneous/bilinear.cpp', @@ -60,7 +61,9 @@ 'miscellaneous/style_parser.cpp', 'miscellaneous/text_conversions.cpp', 'miscellaneous/thread.cpp', + 'miscellaneous/thread_local.cpp', 'miscellaneous/tile.cpp', + 'miscellaneous/timer.cpp', 'miscellaneous/token.cpp', 'miscellaneous/transform.cpp', 'miscellaneous/work_queue.cpp', @@ -96,14 +99,12 @@ ], 'libraries': [ '<@(gtest_static_libs)', - '<@(libuv_static_libs)', '<@(sqlite_static_libs)', '<@(geojsonvt_static_libs)', ], 'variables': { 'cflags_cc': [ '<@(gtest_cflags)', - '<@(libuv_cflags)', '<@(opengl_cflags)', '<@(boost_cflags)', '<@(sqlite_cflags)', @@ -114,7 +115,6 @@ ], 'ldflags': [ '<@(gtest_ldflags)', - '<@(libuv_ldflags)', '<@(sqlite_ldflags)', ], },