From b31026302b34172cef1558445509bbb3370c4e24 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 2 Oct 2017 15:41:02 +0300 Subject: [PATCH 1/6] [android] encapsulate gl surfaceview rendering specifics in map renderer subclass - preparation for re-introduction of texture view --- .../com/mapbox/mapboxsdk/maps/MapView.java | 21 ++--- .../renderer/GLSurfaceViewMapRenderer.java | 84 +++++++++++++++++++ .../mapboxsdk/maps/renderer/MapRenderer.java | 54 +++++------- 3 files changed, 113 insertions(+), 46 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/GLSurfaceViewMapRenderer.java 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 e45e8be6ad2..4fc2a1dffc7 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 @@ -27,7 +27,7 @@ import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; -import com.mapbox.mapboxsdk.egl.EGLConfigChooser; +import com.mapbox.mapboxsdk.maps.renderer.GLSurfaceViewMapRenderer; import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; @@ -87,7 +87,7 @@ public class MapView extends FrameLayout { private Bundle savedInstanceState; private final CopyOnWriteArrayList onMapChangedListeners = new CopyOnWriteArrayList<>(); - private GLSurfaceView glSurfaceView; + private MapRenderer mapRenderer; @UiThread public MapView(@NonNull Context context) { @@ -290,12 +290,10 @@ public void onCreate(@Nullable Bundle savedInstanceState) { } private void initialiseDrawingSurface() { - glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); + GLSurfaceView glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); - glSurfaceView.setEGLContextClientVersion(2); - glSurfaceView.setEGLConfigChooser(new EGLConfigChooser()); - MapRenderer mapRenderer = new MapRenderer(getContext(), glSurfaceView) { + GLSurfaceViewMapRenderer mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView) { @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { MapView.this.post(new Runnable() { @@ -313,10 +311,9 @@ public void run() { } }; - glSurfaceView.setRenderer(mapRenderer); - glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY); glSurfaceView.setVisibility(View.VISIBLE); + this.mapRenderer = mapRenderer; nativeMapView = new NativeMapView(this, mapRenderer); nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight()); } @@ -352,8 +349,8 @@ public void onStart() { */ @UiThread public void onResume() { - if (glSurfaceView != null) { - glSurfaceView.onResume(); + if (mapRenderer != null) { + mapRenderer.onResume(); } } @@ -362,8 +359,8 @@ public void onResume() { */ @UiThread public void onPause() { - if (glSurfaceView != null) { - glSurfaceView.onPause(); + if (mapRenderer != null) { + mapRenderer.onPause(); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/GLSurfaceViewMapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/GLSurfaceViewMapRenderer.java new file mode 100644 index 00000000000..d0ec9f9495e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/GLSurfaceViewMapRenderer.java @@ -0,0 +1,84 @@ +package com.mapbox.mapboxsdk.maps.renderer; + +import android.content.Context; +import android.opengl.GLSurfaceView; + +import com.mapbox.mapboxsdk.egl.EGLConfigChooser; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY; + +/** + * The {@link GLSurfaceViewMapRenderer} encapsulates the GL thread and + * {@link GLSurfaceView} specifics to render the map. + * + * @see MapRenderer + */ +public class GLSurfaceViewMapRenderer extends MapRenderer implements GLSurfaceView.Renderer { + + private final GLSurfaceView glSurfaceView; + + public GLSurfaceViewMapRenderer(Context context, GLSurfaceView glSurfaceView) { + super(context); + this.glSurfaceView = glSurfaceView; + glSurfaceView.setEGLContextClientVersion(2); + glSurfaceView.setEGLConfigChooser(new EGLConfigChooser()); + glSurfaceView.setRenderer(this); + glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY); + } + + @Override + public void onPause() { + if (glSurfaceView != null) { + glSurfaceView.onPause(); + } + } + + @Override + public void onResume() { + if (glSurfaceView != null) { + glSurfaceView.onResume(); + } + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + super.onSurfaceCreated(gl, config); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + super.onSurfaceChanged(gl, width, height); + } + + @Override + public void onDrawFrame(GL10 gl) { + super.onDrawFrame(gl); + } + + /** + * May be called from any thread. + *

+ * Called from the renderer frontend to schedule a render. + */ + @Override + public void requestRender() { + glSurfaceView.requestRender(); + } + + /** + * May be called from any thread. + *

+ * Schedules work to be performed on the MapRenderer thread. + * + * @param runnable the runnable to execute + */ + @Override + public void queueEvent(Runnable runnable) { + glSurfaceView.queueEvent(runnable); + } + + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java index 3f43522e018..a7c0fa3778f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -1,7 +1,7 @@ package com.mapbox.mapboxsdk.maps.renderer; import android.content.Context; -import android.opengl.GLSurfaceView; +import android.support.annotation.CallSuper; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.storage.FileSource; @@ -16,17 +16,14 @@ * render on the one end and acts as a scheduler to request work to * be performed on the GL thread on the other. */ -public class MapRenderer implements GLSurfaceView.Renderer, MapRendererScheduler { +public abstract class MapRenderer implements MapRendererScheduler { // Holds the pointer to the native peer after initialisation private long nativePtr = 0; - private final GLSurfaceView glSurfaceView; - private MapboxMap.OnFpsChangedListener onFpsChangedListener; - public MapRenderer(Context context, GLSurfaceView glSurfaceView) { - this.glSurfaceView = glSurfaceView; + public MapRenderer(Context context) { FileSource fileSource = FileSource.getInstance(context); float pixelRatio = context.getResources().getDisplayMetrics().density; @@ -36,17 +33,26 @@ public MapRenderer(Context context, GLSurfaceView glSurfaceView) { nativeInitialize(this, fileSource, pixelRatio, programCacheDir); } + public void onPause() { + // Implement if needed + } + + public void onResume() { + // Implement if needed + } + + public void setOnFpsChangedListener(MapboxMap.OnFpsChangedListener listener) { onFpsChangedListener = listener; } - @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { + @CallSuper + protected void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeOnSurfaceCreated(); } - @Override - public void onSurfaceChanged(GL10 gl, int width, int height) { + @CallSuper + protected void onSurfaceChanged(GL10 gl, int width, int height) { if (width < 0) { throw new IllegalArgumentException("fbWidth cannot be negative."); } @@ -69,8 +75,8 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { nativeOnSurfaceChanged(width, height); } - @Override - public void onDrawFrame(GL10 gl) { + @CallSuper + protected void onDrawFrame(GL10 gl) { nativeRender(); if (onFpsChangedListener != null) { @@ -78,28 +84,6 @@ public void onDrawFrame(GL10 gl) { } } - /** - * May be called from any thread. - *

- * Called from the renderer frontend to schedule a render. - */ - @Override - public void requestRender() { - glSurfaceView.requestRender(); - } - - /** - * May be called from any thread. - *

- * Schedules work to be performed on the MapRenderer thread. - * - * @param runnable the runnable to execute - */ - @Override - public void queueEvent(Runnable runnable) { - glSurfaceView.queueEvent(runnable); - } - /** * May be called from any thread. *

@@ -109,6 +93,7 @@ public void queueEvent(Runnable runnable) { * @param runnable the runnable to execute * @see MapRendererRunnable */ + @CallSuper void queueEvent(MapRendererRunnable runnable) { this.queueEvent((Runnable) runnable); } @@ -118,6 +103,7 @@ private native void nativeInitialize(MapRenderer self, float pixelRatio, String programCacheDir); + @CallSuper @Override protected native void finalize() throws Throwable; From 9a16934ab19b31f5e03f8092b15f36dbeb943827 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Tue, 31 Oct 2017 12:31:13 +0200 Subject: [PATCH 2/6] [android] move egl under renderer package --- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 2 +- .../{ => maps/renderer}/egl/EGLConfigChooser.java | 2 +- .../{ => maps/renderer}/egl/EGLConfigException.java | 2 +- .../GLSurfaceViewMapRenderer.java | 13 +++++-------- 4 files changed, 8 insertions(+), 11 deletions(-) rename platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/{ => maps/renderer}/egl/EGLConfigChooser.java (99%) rename platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/{ => maps/renderer}/egl/EGLConfigException.java (88%) rename platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/{ => glsurfaceview}/GLSurfaceViewMapRenderer.java (88%) 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 4fc2a1dffc7..c316ea27d8a 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 @@ -27,7 +27,7 @@ import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; -import com.mapbox.mapboxsdk.maps.renderer.GLSurfaceViewMapRenderer; +import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer; import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java similarity index 99% rename from platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java rename to platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java index 7fc70716da3..b6f467708fa 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java @@ -1,4 +1,4 @@ -package com.mapbox.mapboxsdk.egl; +package com.mapbox.mapboxsdk.maps.renderer.egl; import android.opengl.GLSurfaceView; import android.support.annotation.NonNull; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigException.java similarity index 88% rename from platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java rename to platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigException.java index 3f576d0eda4..d5a1c9951f4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigException.java @@ -1,4 +1,4 @@ -package com.mapbox.mapboxsdk.egl; +package com.mapbox.mapboxsdk.maps.renderer.egl; /** * Used for EGL configuration exceptions diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/GLSurfaceViewMapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/GLSurfaceViewMapRenderer.java similarity index 88% rename from platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/GLSurfaceViewMapRenderer.java rename to platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/GLSurfaceViewMapRenderer.java index d0ec9f9495e..ba2e118faaa 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/GLSurfaceViewMapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/GLSurfaceViewMapRenderer.java @@ -1,9 +1,10 @@ -package com.mapbox.mapboxsdk.maps.renderer; +package com.mapbox.mapboxsdk.maps.renderer.glsurfaceview; import android.content.Context; import android.opengl.GLSurfaceView; -import com.mapbox.mapboxsdk.egl.EGLConfigChooser; +import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; +import com.mapbox.mapboxsdk.maps.renderer.egl.EGLConfigChooser; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; @@ -31,16 +32,12 @@ public GLSurfaceViewMapRenderer(Context context, GLSurfaceView glSurfaceView) { @Override public void onPause() { - if (glSurfaceView != null) { - glSurfaceView.onPause(); - } + glSurfaceView.onPause(); } @Override public void onResume() { - if (glSurfaceView != null) { - glSurfaceView.onResume(); - } + glSurfaceView.onResume(); } @Override From a8ca1c5edd2e833432215b70695e0ea9a6b3db14 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 2 Oct 2017 15:41:02 +0300 Subject: [PATCH 3/6] [android] texture view map renderer --- .../com/mapbox/mapboxsdk/maps/MapView.java | 4 + .../mapboxsdk/maps/renderer/MapRenderer.java | 3 + .../textureview/TextureViewMapRenderer.java | 96 ++++ .../textureview/TextureViewRenderThread.java | 442 ++++++++++++++++++ 4 files changed, 545 insertions(+) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java 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 c316ea27d8a..b5546f2cc4e 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 @@ -386,6 +386,10 @@ public void onDestroy() { mapCallback.clearOnMapReadyCallbacks(); nativeMapView.destroy(); nativeMapView = null; + + if (mapRenderer != null) { + mapRenderer.onDestroy(); + } } @Override diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java index a7c0fa3778f..961438ed146 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -41,6 +41,9 @@ public void onResume() { // Implement if needed } + public void onDestroy() { + // Implement if needed + } public void setOnFpsChangedListener(MapboxMap.OnFpsChangedListener listener) { onFpsChangedListener = listener; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java new file mode 100644 index 00000000000..8cd724a8287 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java @@ -0,0 +1,96 @@ +package com.mapbox.mapboxsdk.maps.renderer.textureview; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.view.TextureView; + +import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +/** + * The {@link TextureViewMapRenderer} encapsulates the GL thread and + * {@link TextureView} specifics to render the map. + * + * @see MapRenderer + */ +public class TextureViewMapRenderer extends MapRenderer { + private TextureViewRenderThread renderThread; + + /** + * Create a {@link MapRenderer} for the given {@link TextureView} + * + * @param context the current Context + * @param textureView the TextureView + */ + public TextureViewMapRenderer(@NonNull Context context, @NonNull TextureView textureView) { + super(context); + renderThread = new TextureViewRenderThread(textureView, this); + renderThread.start(); + } + + /** + * Overridden to provide package access + */ + @Override + protected void onSurfaceCreated(GL10 gl, EGLConfig config) { + super.onSurfaceCreated(gl, config); + } + + /** + * Overridden to provide package access + */ + @Override + protected void onSurfaceChanged(GL10 gl, int width, int height) { + super.onSurfaceChanged(gl, width, height); + } + + /** + * Overridden to provide package access + */ + @Override + protected void onDrawFrame(GL10 gl) { + super.onDrawFrame(gl); + } + + /** + * {@inheritDoc} + */ + @Override + public void requestRender() { + renderThread.requestRender(); + } + + /** + * {@inheritDoc} + */ + @Override + public void queueEvent(Runnable runnable) { + renderThread.queueEvent(runnable); + } + + /** + * {@inheritDoc} + */ + @Override + public void onPause() { + renderThread.onPause(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onResume() { + renderThread.onResume(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroy() { + renderThread.onDestroy(); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java new file mode 100644 index 00000000000..3ed55b634ad --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java @@ -0,0 +1,442 @@ +package com.mapbox.mapboxsdk.maps.renderer.textureview; + +import android.graphics.SurfaceTexture; +import android.support.annotation.NonNull; +import android.support.annotation.UiThread; +import android.view.TextureView; + +import com.mapbox.mapboxsdk.maps.renderer.egl.EGLConfigChooser; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL10; + +import timber.log.Timber; + +/** + * The render thread is responsible for managing the communication between the + * ui thread and the render thread it creates. Also, the EGL and GL contexts + * are managed from here. + */ +class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextureListener { + + private final TextureViewMapRenderer mapRenderer; + private final EGLHolder eglHolder; + + // Lock used for synchronization + private final Object lock = new Object(); + + // Guarded by lock + private final ArrayList eventQueue = new ArrayList<>(); + private SurfaceTexture surface; + private int width; + private int height; + private boolean requestRender; + private boolean sizeChanged; + private boolean paused; + private boolean destroyContext; + private boolean destroySurface; + private boolean shouldExit; + private boolean exited; + + /** + * Create a render thread for the given TextureView / Maprenderer combination. + * + * @param textureView the TextureView + * @param mapRenderer the MapRenderer + */ + @UiThread + TextureViewRenderThread(@NonNull TextureView textureView, @NonNull TextureViewMapRenderer mapRenderer) { + textureView.setSurfaceTextureListener(this); + this.mapRenderer = mapRenderer; + this.eglHolder = new EGLHolder(new WeakReference<>(textureView)); + } + + // SurfaceTextureListener methods + + @UiThread + @Override + public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) { + synchronized (lock) { + this.surface = surface; + this.width = width; + this.height = height; + this.requestRender = true; + lock.notifyAll(); + } + } + + @Override + @UiThread + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, final int width, final int height) { + synchronized (lock) { + this.width = width; + this.height = height; + this.sizeChanged = true; + this.requestRender = true; + lock.notifyAll(); + } + } + + @Override + @UiThread + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + synchronized (lock) { + this.surface = null; + this.destroySurface = true; + this.requestRender = false; + lock.notifyAll(); + } + return true; + } + + @Override + @UiThread + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + // Ignored + } + + // MapRenderer delegate methods + + /** + * May be called from any thread + */ + void requestRender() { + synchronized (lock) { + requestRender = true; + lock.notifyAll(); + } + } + + /** + * May be called from any thread + */ + void queueEvent(Runnable runnable) { + if (runnable == null) { + throw new IllegalArgumentException("runnable must not be null"); + } + synchronized (lock) { + eventQueue.add(runnable); + lock.notifyAll(); + } + } + + + @UiThread + void onPause() { + synchronized (lock) { + this.paused = true; + lock.notifyAll(); + } + } + + @UiThread + void onResume() { + synchronized (lock) { + this.paused = false; + lock.notifyAll(); + } + } + + + @UiThread + void onDestroy() { + synchronized (lock) { + this.shouldExit = true; + lock.notifyAll(); + + // Wait for the thread to exit + while (!this.exited) { + try { + lock.wait(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + } + + // Thread implementation + + @Override + public void run() { + try { + + while (true) { + Runnable event = null; + boolean initializeEGL = false; + boolean recreateSurface = false; + int w = -1; + int h = -1; + + // Guarded block + synchronized (lock) { + while (true) { + + if (shouldExit) { + return; + } + + // If any events are scheduled, pop one for processing + if (!eventQueue.isEmpty()) { + event = eventQueue.remove(0); + break; + } + + if (destroySurface) { + eglHolder.destroySurface(); + destroySurface = false; + break; + } + + if (destroyContext) { + eglHolder.destroyContext(); + destroyContext = false; + break; + } + + if (surface != null && !paused && requestRender) { + + w = width; + h = height; + + // Initialize EGL if needed + if (eglHolder.eglContext == EGL10.EGL_NO_CONTEXT) { + initializeEGL = true; + break; + } + + // Check if the size has changed + if (sizeChanged) { + recreateSurface = true; + sizeChanged = false; + break; + } + + // Reset the request render flag now, so we can catch new requests + // while rendering + requestRender = false; + + // Break the guarded loop and continue to process + break; + } + + + // Wait until needed + lock.wait(); + + } // end guarded while loop + + } // end guarded block + + // Run event, if any + if (event != null) { + event.run(); + continue; + } + + GL10 gl = eglHolder.createGL(); + + // Initialize EGL + if (initializeEGL) { + eglHolder.prepare(); + eglHolder.createSurface(); + mapRenderer.onSurfaceCreated(gl, eglHolder.eglConfig); + mapRenderer.onSurfaceChanged(gl, w, h); + continue; + } + + // If the surface size has changed inform the map renderer. + if (recreateSurface) { + eglHolder.createSurface(); + mapRenderer.onSurfaceChanged(gl, w, h); + continue; + } + + // Don't continue without a surface + if (eglHolder.eglSurface == EGL10.EGL_NO_SURFACE) { + continue; + } + + // Time to render a frame + mapRenderer.onDrawFrame(gl); + + // Swap and check the result + int swapError = eglHolder.swap(); + switch (swapError) { + case EGL10.EGL_SUCCESS: + break; + case EGL11.EGL_CONTEXT_LOST: + Timber.w("Context lost. Waiting for re-aquire"); + synchronized (lock) { + surface = null; + destroySurface = true; + destroyContext = true; + } + break; + default: + Timber.w("eglSwapBuffer error: %s. Waiting or new surface", swapError); + // Probably lost the surface. Clear the current one and + // wait for a new one to be set + synchronized (lock) { + surface = null; + destroySurface = true; + } + } + + } + + } catch (InterruptedException err) { + // To be expected + } finally { + // Cleanup + eglHolder.cleanup(); + + // Signal we're done + synchronized (lock) { + this.exited = true; + lock.notifyAll(); + } + } + } + + /** + * Holds the EGL state and offers methods to mutate it. + */ + private static class EGLHolder { + private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + private final WeakReference textureViewWeakRef; + + private EGL10 egl; + private EGLConfig eglConfig; + private EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY; + private EGLContext eglContext = EGL10.EGL_NO_CONTEXT; + private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE; + + EGLHolder(WeakReference textureViewWeakRef) { + this.textureViewWeakRef = textureViewWeakRef; + } + + void prepare() { + this.egl = (EGL10) EGLContext.getEGL(); + + // Only re-initialize display when needed + if (eglDisplay == EGL10.EGL_NO_DISPLAY) { + this.eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + + if (eglDisplay == EGL10.EGL_NO_DISPLAY) { + throw new RuntimeException("eglGetDisplay failed"); + } + + int[] version = new int[2]; + if (!egl.eglInitialize(eglDisplay, version)) { + throw new RuntimeException("eglInitialize failed"); + } + } + + if (textureViewWeakRef == null) { + // No texture view present + eglConfig = null; + eglContext = EGL10.EGL_NO_CONTEXT; + } else /*if (eglContext == EGL10.EGL_NO_CONTEXT)*/ { + eglConfig = new EGLConfigChooser().chooseConfig(egl, eglDisplay); + int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; + eglContext = egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); + } + + if (eglContext == EGL10.EGL_NO_CONTEXT) { + throw new RuntimeException("createContext"); + } + } + + GL10 createGL() { + return (GL10) eglContext.getGL(); + } + + boolean createSurface() { + // The window size has changed, so we need to create a new surface. + destroySurface(); + + // Create an EGL surface we can render into. + TextureView view = textureViewWeakRef.get(); + if (view != null) { + int[] surfaceAttribs = {EGL10.EGL_NONE}; + eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, view.getSurfaceTexture(), surfaceAttribs); + } else { + eglSurface = EGL10.EGL_NO_SURFACE; + } + + if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) { + int error = egl.eglGetError(); + if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { + Timber.e("createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); + } + return false; + } + + return makeCurrent(); + } + + boolean makeCurrent() { + if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + // Could not make the context current, probably because the underlying + // SurfaceView surface has been destroyed. + Timber.w("eglMakeCurrent: %s", egl.eglGetError()); + return false; + } + + return true; + } + + int swap() { + if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) { + return egl.eglGetError(); + } + return EGL10.EGL_SUCCESS; + } + + private void destroySurface() { + if (eglSurface == EGL10.EGL_NO_SURFACE) { + return; + } + + if (!egl.eglDestroySurface(eglDisplay, eglSurface)) { + Timber.e("Could not destroy egl surface. Display %s, Surface %s", eglDisplay, eglSurface); + throw new RuntimeException("eglDestroyContext: " + egl.eglGetError()); + } + } + + private void destroyContext() { + if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) { + return; + } + + if (!egl.eglDestroyContext(eglDisplay, eglContext)) { + Timber.e("Could not destroy egl context. Display %s, Context %s", eglDisplay, eglContext); + throw new RuntimeException("eglDestroyContext: " + egl.eglGetError()); + } + } + + private void terminate() { + if (eglDisplay != EGL10.EGL_NO_DISPLAY) { + if (!egl.eglTerminate(eglDisplay)) { + Timber.w("Could not terminate egl. Display %s", eglDisplay); + } + eglDisplay = EGL10.EGL_NO_DISPLAY; + } + } + + void cleanup() { + destroySurface(); + destroyContext(); + terminate(); + } + } +} From 4fc8a466ac695293d902639019041092f23452ef Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Tue, 31 Oct 2017 18:12:04 +0200 Subject: [PATCH 4/6] [android] bring back texture view --- .../com/mapbox/mapboxsdk/maps/MapView.java | 66 +++++++++++++------ .../mapboxsdk/maps/MapboxMapOptions.java | 37 ++++++++++- .../src/main/res-public/values/public.xml | 3 + .../src/main/res/values/attrs.xml | 3 + 4 files changed, 85 insertions(+), 24 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 b5546f2cc4e..ba09df2cbe7 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 @@ -15,6 +15,7 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.TextureView; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -29,6 +30,7 @@ import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer; import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; +import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; @@ -139,7 +141,7 @@ public void onGlobalLayout() { } else { getViewTreeObserver().removeGlobalOnLayoutListener(this); } - initialiseDrawingSurface(); + initialiseDrawingSurface(options); } }); } @@ -289,31 +291,53 @@ public void onCreate(@Nullable Bundle savedInstanceState) { } } - private void initialiseDrawingSurface() { - GLSurfaceView glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); - glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); + private void initialiseDrawingSurface(MapboxMapOptions options) { + if (options.getTextureMode()) { + TextureView textureView = new TextureView(getContext()); + mapRenderer = new TextureViewMapRenderer(getContext(), textureView) { + @Override + protected void onSurfaceCreated(GL10 gl, EGLConfig config) { + MapView.this.post(new Runnable() { + @Override + public void run() { + // Initialise only once + if (mapboxMap == null) { + initialiseMap(); + mapboxMap.onStart(); + } + } + }); - GLSurfaceViewMapRenderer mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView) { - @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - MapView.this.post(new Runnable() { - @Override - public void run() { - // Initialise only once - if (mapboxMap == null) { - initialiseMap(); - mapboxMap.onStart(); + super.onSurfaceCreated(gl, config); + } + }; + addView(textureView, 0); + } else { + GLSurfaceView glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); + glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); + + mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView) { + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + MapView.this.post(new Runnable() { + @Override + public void run() { + // Initialise only once + if (mapboxMap == null) { + initialiseMap(); + mapboxMap.onStart(); + } } - } - }); + }); - super.onSurfaceCreated(gl, config); - } - }; + super.onSurfaceCreated(gl, config); + } + }; - glSurfaceView.setVisibility(View.VISIBLE); + glSurfaceView.setVisibility(View.VISIBLE); + + } - this.mapRenderer = mapRenderer; nativeMapView = new NativeMapView(this, mapRenderer); nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight()); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java index 7b979f5563a..f26e0b9d3b2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java @@ -85,6 +85,8 @@ public class MapboxMapOptions implements Parcelable { private String apiBaseUrl; + private boolean textureMode; + private String style; /** @@ -152,7 +154,7 @@ private MapboxMapOptions(Parcel in) { style = in.readString(); apiBaseUrl = in.readString(); - + textureMode = in.readByte() != 0; prefetchesTiles = in.readByte() != 0; zMediaOverlay = in.readByte() != 0; } @@ -296,6 +298,8 @@ public static MapboxMapOptions createFromAttributes(@NonNull Context context, @N ColorUtils.getPrimaryColor(context))); mapboxMapOptions.myLocationAccuracyThreshold( typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0)); + mapboxMapOptions.textureMode( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false)); mapboxMapOptions.setPrefetchesTiles( typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true)); mapboxMapOptions.renderSurfaceOnTop( @@ -698,13 +702,30 @@ public MapboxMapOptions myLocationAccuracyThreshold(float myLocationAccuracyThre return this; } + /** + * Enable {@link android.view.TextureView} as rendered surface. + *

+ * Since the 5.2.0 release we replaced our TextureView with an {@link android.opengl.GLSurfaceView} + * implementation. Enabling this option will use the {@link android.view.TextureView} instead. + * {@link android.view.TextureView} can be useful in situations where you need to animate, scale + * or transform the view. This comes at a siginficant performance penalty and should not be considered + * unless absolutely needed. + *

+ * + * @param textureMode True to enable texture mode + * @return This + */ + public MapboxMapOptions textureMode(boolean textureMode) { + this.textureMode = textureMode; + return this; + } + /** * Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render * a low resolution preview while more detailed tiles are loaded. * Enabled by default * * @param enable true to enable - * * @return This */ public MapboxMapOptions setPrefetchesTiles(boolean enable) { @@ -1049,6 +1070,15 @@ public boolean getDebugActive() { return debugActive; } + /** + * Returns true if TextureView is being used the render view. + * + * @return True if TextureView is used. + */ + public boolean getTextureMode() { + return textureMode; + } + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public MapboxMapOptions createFromParcel(Parcel in) { return new MapboxMapOptions(in); @@ -1112,7 +1142,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(style); dest.writeString(apiBaseUrl); - + dest.writeByte((byte) (textureMode ? 1 : 0)); dest.writeByte((byte) (prefetchesTiles ? 1 : 0)); dest.writeByte((byte) (zMediaOverlay ? 1 : 0)); } @@ -1289,6 +1319,7 @@ public int hashCode() { result = 31 * result + (myLocationAccuracyThreshold != +0.0f ? Float.floatToIntBits(myLocationAccuracyThreshold) : 0); result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0); + result = 31 * result + (textureMode ? 1 : 0); result = 31 * result + (style != null ? style.hashCode() : 0); result = 31 * result + (prefetchesTiles ? 1 : 0); result = 31 * result + (zMediaOverlay ? 1 : 0); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml index 40045f851fb..f406f4d042a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml @@ -72,6 +72,9 @@ + + + diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml index b673224094f..2a4c2fe746b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml @@ -114,6 +114,9 @@ + + + From 04ac36318c6f5a0942434168e84231e68e922d72 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Wed, 1 Nov 2017 11:43:27 +0200 Subject: [PATCH 5/6] [android] test app - texture view test activities --- .../com/mapbox/mapboxsdk/maps/MapView.java | 3 +- .../src/main/AndroidManifest.xml | 23 ++ .../TextureViewAnimationActivity.java | 157 ++++++++++ .../TextureViewDebugModeActivity.java | 293 ++++++++++++++++++ .../TextureViewResizeActivity.java | 107 +++++++ .../layout/activity_textureview_animate.xml | 29 ++ .../activity_textureview_debug_mode.xml | 98 ++++++ .../layout/activity_textureview_resize.xml | 29 ++ .../src/main/res/values/categories.xml | 1 + .../src/main/res/values/descriptions.xml | 3 + .../src/main/res/values/titles.xml | 3 + 11 files changed, 744 insertions(+), 2 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml 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 ba09df2cbe7..c025a119b79 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 @@ -28,8 +28,8 @@ import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; -import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer; import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; +import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer; import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; @@ -50,7 +50,6 @@ import timber.log.Timber; -import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 7f955cb45c3..ee26f39f575 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -728,6 +728,29 @@ android:value="@string/category_maplayout"/> + + + + + + + + + + + layerList) { + final LayerListAdapter adapter = new LayerListAdapter(this, layerList); + ListView listView = (ListView) findViewById(R.id.listView); + listView.setAdapter(adapter); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Layer clickedLayer = adapter.getItem(position); + toggleLayerVisibility(clickedLayer); + closeNavigationView(); + } + }); + } + + private void toggleLayerVisibility(Layer layer) { + boolean isVisible = layer.getVisibility().getValue().equals(Property.VISIBLE); + layer.setProperties( + visibility( + isVisible ? Property.NONE : Property.VISIBLE + ) + ); + } + + private void closeNavigationView() { + DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + drawerLayout.closeDrawers(); + } + + private void setupZoomView() { + final TextView textView = (TextView) findViewById(R.id.textZoom); + mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() { + @Override + public void onCameraChange(CameraPosition position) { + textView.setText(String.format(getString(R.string.debug_zoom), position.zoom)); + } + }); + } + + private void setupDebugChangeView() { + FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabDebug); + fabDebug.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mapboxMap != null) { + Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive()); + mapboxMap.cycleDebugOptions(); + } + } + }); + } + + private void setupStyleChangeView() { + FloatingActionButton fabStyles = (FloatingActionButton) findViewById(R.id.fabStyles); + fabStyles.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mapboxMap != null) { + currentStyleIndex++; + if (currentStyleIndex == STYLES.length) { + currentStyleIndex = 0; + } + mapboxMap.setStyleUrl(STYLES[currentStyleIndex], new MapboxMap.OnStyleLoadedListener() { + @Override + public void onStyleLoaded(String style) { + Timber.d("Style loaded %s", style); + } + }); + } + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return actionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + private static class LayerListAdapter extends BaseAdapter { + + private LayoutInflater layoutInflater; + private List layers; + + LayerListAdapter(Context context, List layers) { + this.layoutInflater = LayoutInflater.from(context); + this.layers = layers; + } + + @Override + public int getCount() { + return layers.size(); + } + + @Override + public Layer getItem(int position) { + return layers.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Layer layer = layers.get(position); + View view = convertView; + if (view == null) { + view = layoutInflater.inflate(android.R.layout.simple_list_item_2, parent, false); + ViewHolder holder = new ViewHolder( + (TextView) view.findViewById(android.R.id.text1), + (TextView) view.findViewById(android.R.id.text2) + ); + view.setTag(holder); + } + ViewHolder holder = (ViewHolder) view.getTag(); + holder.text.setText(layer.getClass().getSimpleName()); + holder.subText.setText(layer.getId()); + return view; + } + + private static class ViewHolder { + final TextView text; + final TextView subText; + + ViewHolder(TextView text, TextView subText) { + this.text = text; + this.subText = subText; + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java new file mode 100644 index 00000000000..388774e6c8c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java @@ -0,0 +1,107 @@ +package com.mapbox.mapboxsdk.testapp.activity.textureview; + +import android.os.Bundle; +import android.support.design.widget.CoordinatorLayout; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test resizing a {@link android.view.TextureView} backed map on the fly. + */ +public class TextureViewResizeActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_textureview_resize); + setupToolbar(); + setupMapView(savedInstanceState); + setupFab(); + } + + private void setupToolbar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + } + } + + private void setupMapView(Bundle savedInstanceState) { + mapView = (MapView) findViewById(R.id.mapView); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(MapboxMap mapboxMap) { + TextureViewResizeActivity.this.mapboxMap = mapboxMap; + } + }); + } + + private void setupFab() { + FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabResize); + fabDebug.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mapView != null) { + View parent = findViewById(R.id.coordinator_layout); + int width = parent.getWidth() == mapView.getWidth() ? parent.getWidth() / 2 : parent.getWidth(); + int height = parent.getHeight() == mapView.getHeight() ? parent.getHeight() / 2 : parent.getHeight(); + mapView.setLayoutParams(new CoordinatorLayout.LayoutParams(width, height)); + } + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml new file mode 100644 index 00000000000..de0ef0ef36c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml new file mode 100644 index 00000000000..480de550ad3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml new file mode 100644 index 00000000000..2baa3d39ddd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml index 9ade28ae8d3..dbc6b59db61 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml @@ -14,4 +14,5 @@ Styling Features Storage + Texture View \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index a2bf1d85961..33d96387127 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -64,4 +64,7 @@ Show how to add a marker to a Snapshot Use Android SDK Animators to animate camera position changes Use Android SDK Views as symbols + Use TextureView to render the map + Resize a map rendered on a TextureView + Animate a map rendered on a TextureView \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml index 1fb2f6ba7fd..b90cedc518e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -64,4 +64,7 @@ Map Snapshot with marker Animator animation SymbolGenerator + TextureView debug + TextureView resize + TextureView animation \ No newline at end of file From e552e0bf6d9bc7239d091cf3ff2362fd83456eaa Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Fri, 3 Nov 2017 11:33:00 +0200 Subject: [PATCH 6/6] [android] TextureView - cleanup destruction code --- .../textureview/TextureViewRenderThread.java | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java index 3ed55b634ad..c34833e9ce5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java @@ -213,6 +213,12 @@ public void run() { break; } + // (re-)Initialize EGL Surface if needed + if (eglHolder.eglSurface == EGL10.EGL_NO_SURFACE) { + recreateSurface = true; + break; + } + // Check if the size has changed if (sizeChanged) { recreateSurface = true; @@ -247,7 +253,14 @@ public void run() { // Initialize EGL if (initializeEGL) { eglHolder.prepare(); - eglHolder.createSurface(); + if (!eglHolder.createSurface()) { + synchronized (lock) { + // Cleanup the surface if one could not be created + // and wait for another to be ready. + destroySurface = true; + } + continue; + } mapRenderer.onSurfaceCreated(gl, eglHolder.eglConfig); mapRenderer.onSurfaceChanged(gl, w, h); continue; @@ -345,7 +358,7 @@ void prepare() { // No texture view present eglConfig = null; eglContext = EGL10.EGL_NO_CONTEXT; - } else /*if (eglContext == EGL10.EGL_NO_CONTEXT)*/ { + } else if (eglContext == EGL10.EGL_NO_CONTEXT) { eglConfig = new EGLConfigChooser().chooseConfig(egl, eglDisplay); int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; eglContext = egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); @@ -408,29 +421,33 @@ private void destroySurface() { } if (!egl.eglDestroySurface(eglDisplay, eglSurface)) { - Timber.e("Could not destroy egl surface. Display %s, Surface %s", eglDisplay, eglSurface); - throw new RuntimeException("eglDestroyContext: " + egl.eglGetError()); + Timber.w("Could not destroy egl surface. Display %s, Surface %s", eglDisplay, eglSurface); } + + eglSurface = EGL10.EGL_NO_SURFACE; } private void destroyContext() { - if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) { + if (eglContext == EGL10.EGL_NO_CONTEXT) { return; } if (!egl.eglDestroyContext(eglDisplay, eglContext)) { - Timber.e("Could not destroy egl context. Display %s, Context %s", eglDisplay, eglContext); - throw new RuntimeException("eglDestroyContext: " + egl.eglGetError()); + Timber.w("Could not destroy egl context. Display %s, Context %s", eglDisplay, eglContext); } + + eglContext = EGL10.EGL_NO_CONTEXT; } private void terminate() { - if (eglDisplay != EGL10.EGL_NO_DISPLAY) { - if (!egl.eglTerminate(eglDisplay)) { - Timber.w("Could not terminate egl. Display %s", eglDisplay); - } - eglDisplay = EGL10.EGL_NO_DISPLAY; + if (eglDisplay == EGL10.EGL_NO_DISPLAY) { + return; + } + + if (!egl.eglTerminate(eglDisplay)) { + Timber.w("Could not terminate egl. Display %s", eglDisplay); } + eglDisplay = EGL10.EGL_NO_DISPLAY; } void cleanup() {