diff options
author | Ivo van Dongen <info@ivovandongen.nl> | 2017-01-31 13:43:37 +0200 |
---|---|---|
committer | Tobrun <tobrun.van.nuland@gmail.com> | 2017-02-10 16:37:53 +0100 |
commit | 35b810dd6ddfc5e6f1a635b2682b3add5ec5d5bb (patch) | |
tree | 12a453473e605f389a963cc4a752eb667ff28316 | |
parent | 9b05af8f45f669dc1f79938a1112adac3db7ca00 (diff) | |
download | qtlocation-mapboxgl-35b810dd6ddfc5e6f1a635b2682b3add5ec5d5bb.tar.gz |
[android] render on gl thread
10 files changed, 301 insertions, 724 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index efc1001467..11928615d6 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -53,6 +53,8 @@ import java.util.List; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; +import timber.log.Timber; + import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY; /** @@ -69,15 +71,18 @@ import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY; * <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. */ -public class MapView extends FrameLayout implements GLSurfaceView.Renderer { +public class MapView extends FrameLayout { private NativeMapView nativeMapView; private boolean destroyed; - private boolean hasSurface = false; private GLSurfaceView glSurfaceView; + private CompassView compassView; + private ImageView attrView; + private View logoView; + private MyLocationView myLocationView; private MapboxMap mapboxMap; - private MapCallback mapCallback; + private MapCallback mapCallback = new MapCallback(); private boolean onStartCalled; private boolean onStopCalled; @@ -87,6 +92,8 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { private ConnectivityReceiver connectivityReceiver; + private MapboxMapOptions mapboxMapOptions; + @UiThread public MapView(@NonNull Context context) { super(context); @@ -112,21 +119,89 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { } private void initialise(@NonNull final Context context, @NonNull final MapboxMapOptions options) { + Timber.i("Initialize"); + this.mapboxMapOptions = options; + + // in IDE, show preview map if (isInEditMode()) { - // in IDE, show preview map LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_preview, this); return; } + //Setup basic view properties + setupViewProperties(); + // inflate view View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this); - CompassView compassView = (CompassView) view.findViewById(R.id.compassView); - MyLocationView myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); - ImageView attrView = (ImageView) view.findViewById(R.id.attributionView); - initialiseDrawingSurface(); + compassView = (CompassView) view.findViewById(R.id.compassView); + myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); + attrView = (ImageView) view.findViewById(R.id.attributionView); + logoView = view.findViewById(R.id.logoView); + + glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); + glSurfaceView.setEGLConfigChooser(8, 8, 8, 0 /** TODO: What alpha value do we need here?? */, 16, 8); + glSurfaceView.setEGLContextClientVersion(2); + glSurfaceView.setRenderer(new GLSurfaceView.Renderer() { + @Override + public void onSurfaceCreated(final GL10 gl, EGLConfig eglConfig) { + Timber.i("[%s] onSurfaceCreated", Thread.currentThread().getName()); + glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY); + + // Create native Map object + nativeMapView = new NativeMapView(MapView.this); + + //Continue configuring the map view on the main thread + MapView.this.post(new Runnable() { + @Override + public void run() { + onNativeMapViewReady(); + } + }); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + Timber.i("[%s] onSurfaceChanged %sx%s", Thread.currentThread().getName(), width, height); + // Sets the current view port to the new size. + gl.glViewport(0, 0, width, height); + nativeMapView.onViewportChanged(width, height); + } + + @Override + public void onDrawFrame(GL10 gl) { + Timber.i("[%s] onDrawFrame", Thread.currentThread().getName()); + nativeMapView.render(); + } + }); + + } + + private void setupViewProperties() { + // allow onDraw invocation + setWillNotDraw(false); + + // Ensure this view is interactable + setClickable(true); + setLongClickable(true); + setFocusable(true); + setFocusableInTouchMode(true); + requestDisallowInterceptTouchEvent(true); + } - // create native Map object - nativeMapView = new NativeMapView(this); +// private void loopRender() { +// postDelayed(new Runnable() { +// @Override +// public void run() { +// //glSurfaceView.queueEvent(renderRunnable); +// onInvalidate(); +//// nativeMapView.update(); +// //onInvalidate(); +// loopRender(); +// } +// }, 500); +// } + + protected void onNativeMapViewReady() { // callback for focal point invalidation FocalPointInvalidator focalPoint = new FocalPointInvalidator(compassView); @@ -139,7 +214,7 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { // setup components for MapboxMap creation Projection proj = new Projection(nativeMapView); - UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, view.findViewById(R.id.logoView)); + UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, logoView); TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator); MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint); MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer)); @@ -149,56 +224,23 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { registerTouchListener, annotations); // user input - mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, annotations); + mapGestureDetector = new MapGestureDetector(getContext(), transform, proj, uiSettings, trackingSettings, annotations); mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings); mapZoomButtonController = new MapZoomButtonController(this, uiSettings, transform); // inject widgets with MapboxMap compassView.setMapboxMap(mapboxMap); myLocationView.setMapboxMap(mapboxMap); - attrView.setOnClickListener(new AttributionOnClickListener(context, transform)); - - // Ensure this view is interactable - setClickable(true); - setLongClickable(true); - setFocusable(true); - setFocusableInTouchMode(true); - requestDisallowInterceptTouchEvent(true); - - // allow onDraw invocation - setWillNotDraw(false); + attrView.setOnClickListener(new AttributionOnClickListener(getContext(), transform)); // notify Map object about current connectivity state nativeMapView.setReachability(isConnected()); // initialise MapboxMap - mapboxMap.initialise(context, options); - } - - private void initialiseDrawingSurface() { - glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); - glSurfaceView.setEGLContextClientVersion(2); - glSurfaceView.setRenderer(this); - glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY); - } - - @Override - public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { - nativeMapView.initializeDisplay(); - nativeMapView.initializeContext(); - nativeMapView.createSurface(glSurfaceView.getHolder().getSurface()); - hasSurface = true; - } - - @Override - public void onSurfaceChanged(GL10 gl10, int i, int i1) { - nativeMapView.resizeView(i, i1); - nativeMapView.resizeFramebuffer(i, i1); - } - - @Override - public void onDrawFrame(GL10 gl10) { + mapboxMap.initialise(getContext(), mapboxMapOptions); + addOnMapChangedListener(mapCallback); + mapboxMap.onStart(); } // @@ -218,7 +260,7 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { */ @UiThread public void onCreate(@Nullable Bundle savedInstanceState) { - nativeMapView.setAccessToken(Mapbox.getAccessToken()); + //nativeMapView.setAccessToken(Mapbox.getAccessToken()); if (savedInstanceState == null) { MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapLoadEvent()); @@ -226,7 +268,6 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { mapboxMap.onRestoreInstanceState(savedInstanceState); } - addOnMapChangedListener(mapCallback = new MapCallback(mapboxMap)); } /** @@ -249,7 +290,6 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { public void onStart() { onStartCalled = true; registerConnectivityReceiver(); - mapboxMap.onStart(); glSurfaceView.onResume(); } @@ -296,12 +336,17 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { } destroyed = true; - hasSurface = false; - nativeMapView.terminateContext(); - nativeMapView.terminateDisplay(); - nativeMapView.destroySurface(); - nativeMapView.destroy(); - nativeMapView = null; + //nativeMapView.terminateContext(); + //nativeMapView.terminateDisplay(); + //nativeMapView.destroySurface(); + + glSurfaceView.queueEvent(new Runnable() { + @Override + public void run() { + nativeMapView.destroy(); + nativeMapView = null; + } + }); } private void registerConnectivityReceiver() { @@ -435,28 +480,34 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { // Rendering // - // Called when the map needs to be rerendered - // Called via JNI from NativeMapView + /** + * Called when the map needs to be re-rendered + * Called via JNI from NativeMapView + * <p> + * May be called from any thread + */ protected void onInvalidate() { + Timber.i("onInvalidate"); + //glSurfaceView.onInvalidate(); + //XXX Don't need this right? postInvalidate(); postInvalidate(); } + protected void requestRender() { + if (glSurfaceView != null) { + glSurfaceView.requestRender(); + } + } + @Override public void onDraw(Canvas canvas) { + Timber.i("onDraw"); super.onDraw(canvas); if (isInEditMode()) { return; } - if (destroyed) { - return; - } - - if (!hasSurface) { - return; - } - - nativeMapView.render(); + //glSurfaceView.queueEvent(renderRunnable); } @Override @@ -466,7 +517,9 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { } if (!isInEditMode()) { - nativeMapView.resizeView(width, height); + if (nativeMapView != null) { + //XXX This should happen through GLSurfaceView#Renderer callbacks nativeMapView.resizeView(width, height); + } } } @@ -488,7 +541,9 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { if (isInEditMode()) { return; } - mapZoomButtonController.setVisible(visibility == View.VISIBLE); + if (mapZoomButtonController != null) { + mapZoomButtonController.setVisible(visibility == View.VISIBLE); + } } // @@ -501,7 +556,7 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { // Called when an action we are listening to in the manifest has been sent @Override public void onReceive(Context context, Intent intent) { - if (!destroyed && intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + if (nativeMapView != null && !destroyed && intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { nativeMapView.setReachability(!intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)); } } @@ -929,16 +984,11 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { } } - private static class MapCallback implements OnMapChangedListener { + private class MapCallback implements OnMapChangedListener { - private final MapboxMap mapboxMap; private final List<OnMapReadyCallback> onMapReadyCallbackList = new ArrayList<>(); private boolean initialLoad = true; - MapCallback(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - } - @Override public void onMapChanged(@MapChange int change) { if (change == DID_FINISH_LOADING_STYLE && initialLoad) { @@ -971,4 +1021,14 @@ public class MapView extends FrameLayout implements GLSurfaceView.Renderer { onMapReadyCallbackList.add(callback); } } + + private class RenderRunnable implements Runnable { + @Override + public void run() { + if (destroyed) { + return; + } + nativeMapView.render(); + } + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 1d260cacf3..5069a25d69 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -68,6 +68,8 @@ final class NativeMapView { // public NativeMapView(MapView mapView) { + this.mapView = mapView; + Context context = mapView.getContext(); String dataPath = OfflineManager.getDatabasePath(context); @@ -95,7 +97,10 @@ final class NativeMapView { throw new IllegalArgumentException("totalMemory cannot be negative."); } onMapChangedListeners = new CopyOnWriteArrayList<>(); - this.mapView = mapView; + +// if (Looper.myLooper() == null) { +// Looper.prepare(); +// } nativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath, pixelRatio, availableProcessors, totalMemory); } @@ -103,84 +108,10 @@ final class NativeMapView { // Methods // - private boolean isDestroyedOn(String callingMethod) { - if (destroyed && !TextUtils.isEmpty(callingMethod)) { - Timber.e(String.format(MapboxConstants.MAPBOX_LOCALE, - "You're calling `%s` after the `MapView` was destroyed, were you invoking it after `onDestroy()`?", - callingMethod)); - } - return destroyed; - } - - public void destroy() { - nativeDestroy(nativeMapViewPtr); - nativeMapViewPtr = 0; - mapView = null; - destroyed = true; - } - - public void initializeDisplay() { - if (isDestroyedOn("initializeDisplay")) { - return; - } - nativeInitializeDisplay(nativeMapViewPtr); - } - - public void terminateDisplay() { - if (isDestroyedOn("terminateDisplay")) { - return; - } - nativeTerminateDisplay(nativeMapViewPtr); - } - - public void initializeContext() { - if (isDestroyedOn("initializeContext")) { - return; - } - nativeInitializeContext(nativeMapViewPtr); - } - - public void terminateContext() { - if (isDestroyedOn("terminateContext")) { - return; - } - nativeTerminateContext(nativeMapViewPtr); - } - - public void createSurface(Surface surface) { - if (isDestroyedOn("createSurface")) { + public void onViewportChanged(int width, int height) { + if (isDestroyedOn("onViewportChanged")) { return; } - nativeCreateSurface(nativeMapViewPtr, surface); - } - - public void destroySurface() { - if (isDestroyedOn("destroySurface")) { - return; - } - nativeDestroySurface(nativeMapViewPtr); - } - - public void update() { - if (isDestroyedOn("update")) { - return; - } - nativeUpdate(nativeMapViewPtr); - } - - public void render() { - if (isDestroyedOn("render")) { - return; - } - nativeRender(nativeMapViewPtr); - } - - public void resizeView(int width, int height) { - if (isDestroyedOn("resizeView")) { - return; - } - width = (int) (width / pixelRatio); - height = (int) (height / pixelRatio); if (width < 0) { throw new IllegalArgumentException("width cannot be negative."); @@ -203,31 +134,39 @@ final class NativeMapView { + "capping value at 65535 instead of " + height); height = 65535; } - nativeViewResize(nativeMapViewPtr, width, height); + + nativeOnViewportChanged(nativeMapViewPtr, width, height); } - public void resizeFramebuffer(int fbWidth, int fbHeight) { - if (isDestroyedOn("resizeFramebuffer")) { - return; - } - if (fbWidth < 0) { - throw new IllegalArgumentException("fbWidth cannot be negative."); + private boolean isDestroyedOn(String callingMethod) { + if (destroyed && !TextUtils.isEmpty(callingMethod)) { + Timber.e(String.format(MapboxConstants.MAPBOX_LOCALE, + "You're calling `%s` after the `MapView` was destroyed, were you invoking it after `onDestroy()`?", + callingMethod)); } + return destroyed; + } - if (fbHeight < 0) { - throw new IllegalArgumentException("fbHeight cannot be negative."); - } + public void destroy() { + nativeDestroy(nativeMapViewPtr); + nativeMapViewPtr = 0; + mapView = null; + destroyed = true; + } - if (fbWidth > 65535) { - throw new IllegalArgumentException( - "fbWidth cannot be greater than 65535."); + public void update() { + if (isDestroyedOn("update")) { + return; } + nativeUpdate(nativeMapViewPtr); + } - if (fbHeight > 65535) { - throw new IllegalArgumentException( - "fbHeight cannot be greater than 65535."); + public void render() { + Timber.i("Render"); + if (isDestroyedOn("render")) { + return; } - nativeFramebufferResize(nativeMapViewPtr, fbWidth, fbHeight); + nativeRender(nativeMapViewPtr); } public void addClass(String clazz) { @@ -329,6 +268,7 @@ final class NativeMapView { } public void moveBy(double dx, double dy, long duration) { + Timber.i("Move by %sx%s - %s", dx, dy, duration); if (isDestroyedOn("moveBy")) { return; } @@ -343,6 +283,7 @@ final class NativeMapView { } public void setLatLng(LatLng latLng, long duration) { + Timber.i("setLatLng %sx%s - %s", latLng.getLatitude(), latLng.getLongitude(), duration); if (isDestroyedOn("setLatLng")) { return; } @@ -372,6 +313,7 @@ final class NativeMapView { } public void setPitch(double pitch, long duration) { + Timber.i("setLatLng %s - %s", pitch, duration); if (isDestroyedOn("setPitch")) { return; } @@ -926,14 +868,29 @@ final class NativeMapView { // Callbacks // + /** + * Called through JNI when the map needs to be re-rendered + */ protected void onInvalidate() { + Timber.i("onInvalidate"); mapView.onInvalidate(); } - protected void onMapChanged(int rawChange) { + protected void wakeCallback() { + Timber.i("wake!"); + mapView.requestRender(); + } + + protected void onMapChanged(final int rawChange) { + Timber.i("onMapChanged: %s", rawChange); if (onMapChangedListeners != null) { - for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { - onMapChangedListener.onMapChanged(rawChange); + for (final MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { + mapView.post(new Runnable() { + @Override + public void run() { + onMapChangedListener.onMapChanged(rawChange); + } + }); } } } @@ -974,9 +931,7 @@ final class NativeMapView { private native void nativeRender(long nativeMapViewPtr); - private native void nativeViewResize(long nativeMapViewPtr, int width, int height); - - private native void nativeFramebufferResize(long nativeMapViewPtr, int fbWidth, int fbHeight); + private native void nativeOnViewportChanged(long nativeMapViewPtr, int width, int height); private native void nativeAddClass(long nativeMapViewPtr, String clazz); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 318f891127..fee8f93e90 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -13,7 +13,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxsdk.testapp" - minSdkVersion 15 + minSdkVersion 16 targetSdkVersion 25 versionCode 11 versionName "5.0.0" @@ -86,6 +86,11 @@ dependencies { androidTestCompile "com.android.support.test:rules:${testRunnerVersion}" androidTestCompile "com.android.support.test.espresso:espresso-core:${espressoVersion}" androidTestCompile "com.android.support.test.espresso:espresso-intents:${espressoVersion}" + + //FPS Meter + debugCompile "com.github.brianPlummer:tinydancer:0.1.0" + releaseCompile "com.github.brianPlummer:tinydancer-noop:0.1.0" + testCompile "com.github.brianPlummer:tinydancer-noop:0.1.0" } apply from: 'gradle-make.gradle' diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java index a10c6eaad3..c6d95ecfd5 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java @@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.testapp; import android.app.Application; import android.os.StrictMode; +import com.codemonkeylabs.fpslibrary.TinyDancer; import com.mapbox.mapboxsdk.Mapbox; import com.squareup.leakcanary.LeakCanary; @@ -38,6 +39,8 @@ public class MapboxApplication extends Application { .build()); Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token)); + + TinyDancer.create().show(this); } private void initializeLogger() { diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index b6113e1d3c..3acb987445 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -59,6 +59,7 @@ std::string apkPath; std::string androidRelease; jni::jmethodID* onInvalidateId = nullptr; +jni::jmethodID* wakeCallbackId = nullptr; jni::jmethodID* onMapChangedId = nullptr; jni::jmethodID* onFpsChangedId = nullptr; jni::jmethodID* onSnapshotReadyId = nullptr; @@ -307,61 +308,21 @@ using namespace mbgl::android; using DebugOptions = mbgl::MapDebugOptions; jlong nativeCreate(JNIEnv *env, jni::jobject* obj, jni::jstring* cachePath_, jni::jstring* dataPath_, jni::jstring* apkPath_, jfloat pixelRatio, jint availableProcessors, jlong totalMemory) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreate"); + mbgl::Log::Info(mbgl::Event::JNI, "nativeCreate"); cachePath = std_string_from_jstring(env, cachePath_); dataPath = std_string_from_jstring(env, dataPath_); apkPath = std_string_from_jstring(env, apkPath_); + + mbgl::Log::Info(mbgl::Event::JNI, "Create NativeMapView"); return reinterpret_cast<jlong>(new NativeMapView(env, jni::Unwrap(obj), pixelRatio, availableProcessors, totalMemory)); } void nativeDestroy(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroy"); + mbgl::Log::Info(mbgl::Event::JNI, "nativeDestroy"); assert(nativeMapViewPtr != 0); delete reinterpret_cast<NativeMapView *>(nativeMapViewPtr); } -void nativeInitializeDisplay(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeDisplay"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->initializeDisplay(); -} - -void nativeTerminateDisplay(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateDisplay"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->terminateDisplay(); -} - -void nativeInitializeContext(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeContext"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->initializeContext(); -} - -void nativeTerminateContext(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateContext"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->terminateContext(); -} - -void nativeCreateSurface(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* surface) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreateSurface"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->createSurface(ANativeWindow_fromSurface(env, jni::Unwrap(surface))); -} - -void nativeDestroySurface(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroySurface"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->destroySurface(); -} - void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); @@ -369,31 +330,22 @@ void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { } void nativeRender(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { + mbgl::Log::Info(mbgl::Event::JNI, "nativeRender"); + mbgl::util::RunLoop::Get()->runOnce(); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); nativeMapView->render(); } -void nativeViewResize(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jint width, jint height) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeViewResize"); +void nativeOnViewportChanged(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jint width, jint height) { + mbgl::Log::Info(mbgl::Event::JNI, "nativeViewResize"); assert(nativeMapViewPtr != 0); assert(width >= 0); assert(height >= 0); assert(width <= UINT16_MAX); assert(height <= UINT16_MAX); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->resizeView(width, height); -} - -void nativeFramebufferResize(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jint fbWidth, jint fbHeight) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeFramebufferResize"); - assert(nativeMapViewPtr != 0); - assert(fbWidth >= 0); - assert(fbHeight >= 0); - assert(fbWidth <= UINT16_MAX); - assert(fbHeight <= UINT16_MAX); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->resizeFramebuffer(fbWidth, fbHeight); + nativeMapView->onViewportChanged(width, height); } void nativeRemoveClass(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* clazz) { @@ -1149,7 +1101,7 @@ jni::jobject* nativeGetLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapView // Find the layer mbgl::style::Layer* coreLayer = nativeMapView->getMap().getLayer(std_string_from_jstring(env, layerId)); if (!coreLayer) { - mbgl::Log::Debug(mbgl::Event::JNI, "No layer found"); + mbgl::Log::Info(mbgl::Event::JNI, "No layer found"); return jni::Object<Layer>(); } @@ -1210,7 +1162,7 @@ jni::jobject* nativeGetSource(JNIEnv *env, jni::jobject* obj, jni::jlong nativeM // Find the source mbgl::style::Source* coreSource = nativeMapView->getMap().getSource(std_string_from_jstring(env, sourceId)); if (!coreSource) { - mbgl::Log::Debug(mbgl::Event::JNI, "No source found"); + mbgl::Log::Info(mbgl::Event::JNI, "No source found"); return jni::Object<Source>(); } @@ -1329,7 +1281,7 @@ void listOfflineRegions(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourceP JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { - mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); + mbgl::Log::Info(mbgl::Event::JNI, "Attached."); } if (error) { @@ -1419,7 +1371,7 @@ void createOfflineRegion(JNIEnv *env, jni::jobject* obj, jlong defaultFileSource JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { - mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); + mbgl::Log::Info(mbgl::Event::JNI, "Attached."); } if (error) { @@ -1505,7 +1457,7 @@ void setOfflineRegionObserver(JNIEnv *env, jni::jobject* offlineRegion_, jni::jo } ~Observer() override { - mbgl::Log::Debug(mbgl::Event::JNI, "~Observer()"); + mbgl::Log::Info(mbgl::Event::JNI, "~Observer()"); // Env JNIEnv* env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); @@ -1650,7 +1602,7 @@ void getOfflineRegionStatus(JNIEnv *env, jni::jobject* offlineRegion_, jni::jobj JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { - mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); + mbgl::Log::Info(mbgl::Event::JNI, "Attached."); } if (error) { @@ -1704,7 +1656,7 @@ void deleteOfflineRegion(JNIEnv *env, jni::jobject* offlineRegion_, jni::jobject JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { - mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); + mbgl::Log::Info(mbgl::Event::JNI, "Attached."); } if (error) { @@ -1747,7 +1699,7 @@ void updateOfflineRegionMetadata(JNIEnv *env, jni::jobject* offlineRegion_, jni: JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { - mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); + mbgl::Log::Info(mbgl::Event::JNI, "Attached."); } if (error) { @@ -1776,6 +1728,7 @@ void registerNatives(JavaVM *vm) { jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6); + //For the DefaultFileSource static mbgl::util::RunLoop mainRunLoop; mbgl::android::RegisterNativeHTTPRequest(env); @@ -1861,6 +1814,7 @@ void registerNatives(JavaVM *vm) { jni::jclass& nativeMapViewClass = jni::FindClass(env, "com/mapbox/mapboxsdk/maps/NativeMapView"); onInvalidateId = &jni::GetMethodID(env, nativeMapViewClass, "onInvalidate", "()V"); + wakeCallbackId = &jni::GetMethodID(env, nativeMapViewClass, "wakeCallback","()V"); onMapChangedId = &jni::GetMethodID(env, nativeMapViewClass, "onMapChanged", "(I)V"); onFpsChangedId = &jni::GetMethodID(env, nativeMapViewClass, "onFpsChanged", "(D)V"); onSnapshotReadyId = &jni::GetMethodID(env, nativeMapViewClass, "onSnapshotReady","(Landroid/graphics/Bitmap;)V"); @@ -1870,16 +1824,9 @@ void registerNatives(JavaVM *vm) { jni::RegisterNatives(env, nativeMapViewClass, MAKE_NATIVE_METHOD(nativeCreate, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;FIJ)J"), MAKE_NATIVE_METHOD(nativeDestroy, "(J)V"), - MAKE_NATIVE_METHOD(nativeInitializeDisplay, "(J)V"), - MAKE_NATIVE_METHOD(nativeTerminateDisplay, "(J)V"), - MAKE_NATIVE_METHOD(nativeInitializeContext, "(J)V"), - MAKE_NATIVE_METHOD(nativeTerminateContext, "(J)V"), - MAKE_NATIVE_METHOD(nativeCreateSurface, "(JLandroid/view/Surface;)V"), - MAKE_NATIVE_METHOD(nativeDestroySurface, "(J)V"), MAKE_NATIVE_METHOD(nativeUpdate, "(J)V"), MAKE_NATIVE_METHOD(nativeRender, "(J)V"), - MAKE_NATIVE_METHOD(nativeViewResize, "(JII)V"), - MAKE_NATIVE_METHOD(nativeFramebufferResize, "(JII)V"), + MAKE_NATIVE_METHOD(nativeOnViewportChanged, "(JII)V"), MAKE_NATIVE_METHOD(nativeAddClass, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeRemoveClass, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeHasClass, "(JLjava/lang/String;)Z"), diff --git a/platform/android/src/jni.hpp b/platform/android/src/jni.hpp index d12030f3c1..04bad003f6 100644 --- a/platform/android/src/jni.hpp +++ b/platform/android/src/jni.hpp @@ -17,6 +17,7 @@ extern std::string apkPath; extern std::string androidRelease; extern jmethodID onInvalidateId; +extern jmethodID wakeCallbackId; extern jmethodID onMapChangedId; extern jmethodID onFpsChangedId; extern jmethodID onSnapshotReadyId; diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index e994afc7b8..50fa8944f7 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -1,5 +1,6 @@ #include "native_map_view.hpp" #include "jni.hpp" +#include <jni/jni.hpp> #include <cstdlib> #include <ctime> @@ -19,15 +20,23 @@ #include <mbgl/util/image.hpp> #include "bitmap.hpp" +#include "run_loop_impl.hpp" namespace mbgl { namespace android { -NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio, int availableProcessors_, size_t totalMemory_) +NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float _pixelRatio, int availableProcessors_, size_t totalMemory_) : env(env_), + pixelRatio(_pixelRatio), availableProcessors(availableProcessors_), totalMemory(totalMemory_), + runLoop(std::make_unique<mbgl::util::RunLoop>(mbgl::util::RunLoop::Type::New)), threadPool(4) { + mbgl::Log::Info(mbgl::Event::Android, "NativeMapView::NativeMapView"); + + //Add a wake function to wake the render thread when needed + mbgl::util::RunLoop::Impl* loop = reinterpret_cast<mbgl::util::RunLoop::Impl*>(mbgl::util::RunLoop::getLoopHandle()); + loop->setWakeFunction(std::bind(&NativeMapView::wake, this)); assert(env_ != nullptr); assert(obj_ != nullptr); @@ -63,9 +72,7 @@ NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio, int a } NativeMapView::~NativeMapView() { - terminateContext(); - destroySurface(); - terminateDisplay(); + mbgl::Log::Info(mbgl::Event::Android, "NativeMapView::~NativeMapView"); assert(vm != nullptr); assert(obj != nullptr); @@ -81,9 +88,24 @@ NativeMapView::~NativeMapView() { } mbgl::Size NativeMapView::getFramebufferSize() const { + mbgl::Log::Info(mbgl::Event::Android, "FB size %dx%d", fbWidth, fbHeight); return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) }; } +void NativeMapView::wake() { + mbgl::Log::Info(mbgl::Event::JNI, "Wake callback"); + + JNIEnv *env2; + jboolean detach = attach_jni_thread(theJVM, &env2, "GL Callback Thread"); + if (!detach) { + env2->CallVoidMethod(obj, wakeCallbackId); + if (env2->ExceptionCheck()) { + env2->ExceptionDescribe(); + } + } + detach_jni_thread(theJVM, &env2, detach); +} + void NativeMapView::updateViewBinding() { getContext().bindFramebuffer.setCurrentValue(0); assert(mbgl::gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); @@ -96,59 +118,8 @@ void NativeMapView::bind() { getContext().viewport = { 0, 0, getFramebufferSize() }; } -void NativeMapView::activate() { - if (active++) { - return; - } - - oldDisplay = eglGetCurrentDisplay(); - oldReadSurface = eglGetCurrentSurface(EGL_READ); - oldDrawSurface = eglGetCurrentSurface(EGL_DRAW); - oldContext = eglGetCurrentContext(); - - assert(vm != nullptr); - - if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) { - if (!eglMakeCurrent(display, surface, surface, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - if (!eglSwapInterval(display, 0)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError()); - throw std::runtime_error("eglSwapInterval() failed"); - } - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready"); - } -} - -void NativeMapView::deactivate() { - if (--active) { - return; - } - - assert(vm != nullptr); - - if (oldContext != context && oldContext != EGL_NO_CONTEXT) { - if (!eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - } else if (display != EGL_NO_DISPLAY) { - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready"); - } -} - void NativeMapView::invalidate() { + Log::Info(mbgl::Event::Android, "NativeMapView::invalidate"); assert(vm != nullptr); assert(obj != nullptr); JNIEnv *env2; @@ -162,7 +133,7 @@ void NativeMapView::invalidate() { } void NativeMapView::render() { - BackendScope guard(*this); + mbgl::Log::Info(mbgl::Event::Android, "NativeMapView::render"); if (framebufferSizeChanged) { getContext().viewport = { 0, 0, getFramebufferSize() }; @@ -189,395 +160,20 @@ void NativeMapView::render() { } } - if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) { - if (!eglSwapBuffers(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d", - eglGetError()); - throw std::runtime_error("eglSwapBuffers() failed"); - } - - updateFps(); - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready"); - } + updateFps(); } mbgl::Map &NativeMapView::getMap() { return *map; } mbgl::DefaultFileSource &NativeMapView::getFileSource() { return *fileSource; } -void NativeMapView::initializeDisplay() { - assert(display == EGL_NO_DISPLAY); - assert(config == nullptr); - assert(format < 0); - - display = eglGetCurrentDisplay(); - if (display == EGL_NO_DISPLAY) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError()); - throw std::runtime_error("eglGetDisplay() failed"); - } - - EGLint major, minor; - if (!eglInitialize(display, &major, &minor)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError()); - throw std::runtime_error("eglInitialize() failed"); - } - if ((major <= 1) && (minor < 3)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major, - minor); - throw std::runtime_error("EGL version is too low"); - } - - // Detect if we are in emulator. - const bool inEmulator = []() { - char prop[PROP_VALUE_MAX]; - __system_property_get("ro.kernel.qemu", prop); - return strtol(prop, nullptr, 0) == 1; - }(); - - if (inEmulator) { - // XXX https://code.google.com/p/android/issues/detail?id=78977 - mbgl::Log::Warning(mbgl::Event::Android, "Running SDK in emulator!"); - } - - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglBindAPI(EGL_OPENGL_ES_API) returned error %d", eglGetError()); - throw std::runtime_error("eglBindAPI() failed"); - } - - // Get all configs at least RGB 565 with 16 depth and 8 stencil - EGLint configAttribs[] = { - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_BUFFER_SIZE, 16, - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_DEPTH_SIZE, 16, - EGL_STENCIL_SIZE, 8, - (inEmulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, - (inEmulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, - EGL_NONE - }; - - EGLint numConfigs; - if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d", - eglGetError()); - throw std::runtime_error("eglChooseConfig() failed"); - } - if (numConfigs < 1) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs."); - throw std::runtime_error("eglChooseConfig() failed"); - } - - const auto configs = std::make_unique<EGLConfig[]>(numConfigs); - if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError()); - throw std::runtime_error("eglChooseConfig() failed"); - } - - config = chooseConfig(configs.get(), numConfigs); - if (config == nullptr) { - mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen"); - throw std::runtime_error("No config chosen"); - } - - if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } -} - -void NativeMapView::terminateDisplay() { - if (display != EGL_NO_DISPLAY) { - surface = EGL_NO_SURFACE; - - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - if (!eglTerminate(display)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d", - eglGetError()); - throw std::runtime_error("eglTerminate() failed"); - } - } - - display = EGL_NO_DISPLAY; - config = nullptr; - format = -1; -} - -void NativeMapView::initializeContext() { - assert(display != EGL_NO_DISPLAY); - assert(context == EGL_NO_CONTEXT); - assert(config != nullptr); - - context = eglGetCurrentContext(); - if (context == EGL_NO_CONTEXT) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d", - eglGetError()); - throw std::runtime_error("eglCreateContext() failed"); - } -} - -void NativeMapView::terminateContext() { - if (display != EGL_NO_DISPLAY) { - - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - } - - context = EGL_NO_CONTEXT; -} - -void NativeMapView::createSurface(ANativeWindow *window_) { - assert(window == nullptr); - assert(window_ != nullptr); - window = window_; - - assert(display != EGL_NO_DISPLAY); - assert(surface == EGL_NO_SURFACE); - assert(config != nullptr); - assert(format >= 0); - - ANativeWindow_setBuffersGeometry(window, 0, 0, format); - - surface = eglGetCurrentSurface(EGL_DRAW); - if (surface == EGL_NO_SURFACE) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglCreateWindowSurface() failed"); - } - - if (!firstTime) { - firstTime = true; - - BackendScope guard(*this); - - if (!eglMakeCurrent(display, surface, surface, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - mbgl::gl::InitializeExtensions([] (const char * name) { - return reinterpret_cast<mbgl::gl::glProc>(eglGetProcAddress(name)); - }); - } -} - -void NativeMapView::destroySurface() { - if (surface != EGL_NO_SURFACE) { - if (!eglDestroySurface(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglDestroySurface() failed"); - } - } - - surface = EGL_NO_SURFACE; - - if (window != nullptr) { - ANativeWindow_release(window); - window = nullptr; - } -} - void NativeMapView::scheduleTakeSnapshot() { snapshot = true; } -// Speed -/* -typedef enum { - Format16Bit = 0, - Format32BitNoAlpha = 1, - Format32BitAlpha = 2, - Format24Bit = 3, - Unknown = 4 -} BufferFormat; - -typedef enum { - Format16Depth8Stencil = 0, - Format24Depth8Stencil = 1, -} DepthStencilFormat; -*/ - -// Quality -typedef enum { - Format16Bit = 3, - Format32BitNoAlpha = 1, - Format32BitAlpha = 2, - Format24Bit = 0, - Unknown = 4 -} BufferFormat; - -typedef enum { - Format16Depth8Stencil = 1, - Format24Depth8Stencil = 0, -} DepthStencilFormat; - -// Tuple is <buffer_format, depth_stencil_format, is_not_conformant, is_caveat, config_num, -// config_id> -typedef std::tuple<BufferFormat, DepthStencilFormat, bool, bool, int, EGLConfig> ConfigProperties; - -EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfigs) { - // Create a list of configs that pass our filters - std::list<ConfigProperties> configList; - for (int i = 0; i < numConfigs; i++) { - EGLint caveat, conformant, bits, red, green, blue, alpha, alphaMask, depth, stencil, - sampleBuffers, samples; - - if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - bool configOk = true; - configOk &= (depth == 24) || (depth == 16); - configOk &= stencil == 8; - configOk &= sampleBuffers == 0; - configOk &= samples == 0; - - // Filter our configs first for depth, stencil and anti-aliasing - if (configOk) { - // Work out the config's buffer format - BufferFormat bufferFormat; - if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) { - bufferFormat = Format16Bit; - } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) { - bufferFormat = Format32BitNoAlpha; - } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) { - bufferFormat = Format32BitAlpha; - } else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) { - bufferFormat = Format24Bit; - } else { - bufferFormat = Unknown; - } - - // Work out the config's depth stencil format - DepthStencilFormat depthStencilFormat; - if ((depth == 16) && (stencil == 8)) { - depthStencilFormat = Format16Depth8Stencil; - } else { - depthStencilFormat = Format24Depth8Stencil; - } - - bool isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT; - bool isCaveat = caveat != EGL_NONE; - EGLConfig configId = configs[i]; - - // Ignore formats we don't recognise - if (bufferFormat != Unknown) { - configList.push_back(std::make_tuple(bufferFormat, depthStencilFormat, - isNotConformant, isCaveat, i, configId)); - } - } - } - - if (configList.empty()) { - mbgl::Log::Error(mbgl::Event::OpenGL, "Config list was empty."); - } - - // Sort the configs to find the best one - configList.sort(); - bool isConformant = !std::get<2>(configList.front()); - bool isCaveat = std::get<3>(configList.front()); - EGLConfig configId = std::get<5>(configList.front()); - - if (isCaveat) { - mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat."); - } - if (!isConformant) { - mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant."); - } - - return configId; -} void NativeMapView::notifyMapChange(mbgl::MapChange change) { + mbgl::Log::Info(mbgl::Event::Android, "notifyMapChange"); assert(vm != nullptr); assert(obj != nullptr); @@ -610,7 +206,7 @@ void NativeMapView::updateFps() { if (currentTime - timeElapsed >= 1) { fps = frames / ((currentTime - timeElapsed) / 1E9); - mbgl::Log::Debug(mbgl::Event::Render, "FPS: %4.2f", fps); + mbgl::Log::Info(mbgl::Event::Render, "FPS: %4.2f", fps); timeElapsed = currentTime; frames = 0; } @@ -628,13 +224,20 @@ void NativeMapView::updateFps() { } } +void NativeMapView::onViewportChanged(int w, int h) { + resizeView((int) w / pixelRatio, (int) h / pixelRatio); + resizeFramebuffer(w, h); +} + void NativeMapView::resizeView(int w, int h) { + mbgl::Log::Info(mbgl::Event::Android, "resizeView %ix%i", w, h); width = w; height = h; map->setSize({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); } void NativeMapView::resizeFramebuffer(int w, int h) { + mbgl::Log::Info(mbgl::Event::Android, "resizeFramebuffer %ix%i", w, h); fbWidth = w; fbHeight = h; framebufferSizeChanged = true; diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index e7379700a9..81274f3a24 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -5,12 +5,11 @@ #include <mbgl/map/backend.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/run_loop.hpp> #include <mbgl/storage/default_file_source.hpp> #include <string> #include <jni.h> -#include <android/native_window.h> -#include <EGL/egl.h> namespace mbgl { namespace android { @@ -20,68 +19,57 @@ public: NativeMapView(JNIEnv *env, jobject obj, float pixelRatio, int availableProcessors, size_t totalMemory); virtual ~NativeMapView(); - mbgl::Size getFramebufferSize() const; - void updateViewBinding(); + // mbgl::View // + void bind() override; - void invalidate() override; + // mbgl::Backend // + void invalidate() override; void notifyMapChange(mbgl::MapChange) override; + // JNI // + mbgl::Map &getMap(); mbgl::DefaultFileSource &getFileSource(); - void initializeDisplay(); - void terminateDisplay(); - - void initializeContext(); - void terminateContext(); - - void createSurface(ANativeWindow *window); - void destroySurface(); - void render(); void enableFps(bool enable); - void updateFps(); - void resizeView(int width, int height); - void resizeFramebuffer(int width, int height); + void onViewportChanged(int width, int height); + mbgl::EdgeInsets getInsets() { return insets;} void setInsets(mbgl::EdgeInsets insets_); void scheduleTakeSnapshot(); protected: - void activate() override; - void deactivate() override; + // Unused // -private: - EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs); + void activate() override {}; + void deactivate() override {}; private: - JavaVM *vm = nullptr; - JNIEnv *env = nullptr; - jweak obj = nullptr; + void wake(); + void updateViewBinding(); + mbgl::Size getFramebufferSize() const; - ANativeWindow *window = nullptr; + void resizeView(int width, int height); + void resizeFramebuffer(int width, int height); - EGLDisplay oldDisplay = EGL_NO_DISPLAY; - EGLSurface oldReadSurface = EGL_NO_SURFACE; - EGLSurface oldDrawSurface = EGL_NO_SURFACE; - EGLContext oldContext = EGL_NO_CONTEXT; + void updateFps(); - EGLDisplay display = EGL_NO_DISPLAY; - EGLSurface surface = EGL_NO_SURFACE; - EGLContext context = EGL_NO_CONTEXT; +private: - EGLConfig config = nullptr; - EGLint format = -1; + JavaVM *vm = nullptr; + JNIEnv *env = nullptr; + jweak obj = nullptr; std::string styleUrl; std::string apiKey; - bool firstTime = false; + float pixelRatio; bool fpsEnabled = false; bool snapshot = false; double fps = 0.0; @@ -96,12 +84,12 @@ private: size_t totalMemory = 0; // Ensure these are initialised last + std::unique_ptr<mbgl::util::RunLoop> runLoop; std::unique_ptr<mbgl::DefaultFileSource> fileSource; mbgl::ThreadPool threadPool; std::unique_ptr<mbgl::Map> map; mbgl::EdgeInsets insets; - unsigned active = 0; }; } } diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp index 170b05c23c..9ad9aa1db7 100644 --- a/platform/android/src/run_loop.cpp +++ b/platform/android/src/run_loop.cpp @@ -4,6 +4,7 @@ #include <mbgl/util/thread_context.hpp> #include <mbgl/util/thread_local.hpp> #include <mbgl/util/timer.hpp> +#include <mbgl/util/logging.hpp> #include <android/looper.h> @@ -129,9 +130,16 @@ RunLoop::Impl::~Impl() { } void RunLoop::Impl::wake() { + mbgl::Log::Info(mbgl::Event::Android, "Waking...?"); if (write(fds[PIPE_IN], "\n", 1) == -1) { throw std::runtime_error("Failed to write to file descriptor."); } + + // If the loop has a wake function, call it (assumed to be thread safe) + if (wakeFunction) { + mbgl::Log::Info(mbgl::Event::Android, "Calling wake function"); + wakeFunction(); + } } void RunLoop::Impl::addRunnable(Runnable* runnable) { @@ -208,6 +216,7 @@ RunLoop::~RunLoop() { } LOOP_HANDLE RunLoop::getLoopHandle() { + assert (current.get() != nullptr); return current.get()->impl.get(); } diff --git a/platform/android/src/run_loop_impl.hpp b/platform/android/src/run_loop_impl.hpp index a3efa92a83..b37b597b12 100644 --- a/platform/android/src/run_loop_impl.hpp +++ b/platform/android/src/run_loop_impl.hpp @@ -33,6 +33,11 @@ public: Impl(RunLoop*, RunLoop::Type); ~Impl(); + // Optional wake function (for other wake mechanisms than pipes, eg no java run loop present) + void setWakeFunction(std::function<void()> callback) { + wakeFunction = callback; + } + void wake(); void addRunnable(Runnable*); @@ -49,6 +54,7 @@ private: friend RunLoop; int fds[2]; + std::function<void()> wakeFunction; JNIEnv *env = nullptr; bool detach = false; |