[android] self-contained map renderer
- Isolates the GL thread in a MapRenderer class with a native peer
This commit is contained in:
committed by
Ivo van Dongen
parent
15a47d116a
commit
5d12503302
@@ -28,7 +28,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.GlSurfaceViewRenderThread;
|
||||
import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
|
||||
@@ -286,35 +286,31 @@ public class MapView extends FrameLayout {
|
||||
glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
|
||||
glSurfaceView.setEGLContextClientVersion(2);
|
||||
glSurfaceView.setEGLConfigChooser(new EGLConfigChooser());
|
||||
glSurfaceView.setRenderer(new GLSurfaceView.Renderer() {
|
||||
|
||||
MapRenderer mapRenderer = new MapRenderer(getContext(), glSurfaceView) {
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
MapView.this.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
initialiseMap();
|
||||
mapboxMap.onStart();
|
||||
// Initialise only once
|
||||
if (mapboxMap == null) {
|
||||
initialiseMap();
|
||||
mapboxMap.onStart();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
nativeMapView.onSurfaceCreated(gl, config);
|
||||
super.onSurfaceCreated(gl, config);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||
nativeMapView.onSurfaceChanged(gl, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
nativeMapView.onDrawFrame(gl);
|
||||
}
|
||||
});
|
||||
glSurfaceView.setRenderer(mapRenderer);
|
||||
glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);
|
||||
glSurfaceView.setVisibility(View.VISIBLE);
|
||||
|
||||
nativeMapView.setRenderThread(new GlSurfaceViewRenderThread(glSurfaceView));
|
||||
nativeMapView = new NativeMapView(this, mapRenderer);
|
||||
nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -527,9 +523,7 @@ public class MapView extends FrameLayout {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nativeMapView == null) {
|
||||
nativeMapView = new NativeMapView(this);
|
||||
} else if (mapZoomButtonController != null) {
|
||||
if (mapZoomButtonController != null) {
|
||||
mapZoomButtonController.setVisible(visibility == View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.support.annotation.IntRange;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -20,7 +19,8 @@ import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
|
||||
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
|
||||
import com.mapbox.mapboxsdk.maps.renderer.RenderThread;
|
||||
import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
|
||||
import com.mapbox.mapboxsdk.maps.renderer.MapRendererScheduler;
|
||||
import com.mapbox.mapboxsdk.storage.FileSource;
|
||||
import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException;
|
||||
import com.mapbox.mapboxsdk.style.layers.Filter;
|
||||
@@ -36,13 +36,10 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
// Class that wraps the native methods for convenience
|
||||
final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
final class NativeMapView {
|
||||
|
||||
// Flag to indicating destroy was called
|
||||
private boolean destroyed = false;
|
||||
@@ -53,12 +50,12 @@ final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
// Used for callbacks
|
||||
private MapView mapView;
|
||||
|
||||
// Used to schedule work on the render thread
|
||||
private RenderThread renderThread;
|
||||
|
||||
//Hold a reference to prevent it from being GC'd as long as it's used on the native side
|
||||
private final FileSource fileSource;
|
||||
|
||||
// Used to schedule work on the MapRenderer Thread
|
||||
private MapRendererScheduler scheduler;
|
||||
|
||||
// Device density
|
||||
private final float pixelRatio;
|
||||
|
||||
@@ -73,25 +70,21 @@ final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
// Constructors
|
||||
//
|
||||
|
||||
public NativeMapView(final MapView mapView) {
|
||||
Context context = mapView.getContext();
|
||||
fileSource = FileSource.getInstance(context);
|
||||
|
||||
pixelRatio = context.getResources().getDisplayMetrics().density;
|
||||
public NativeMapView(final MapView mapView, MapRenderer mapRenderer) {
|
||||
this.scheduler = mapRenderer;
|
||||
this.mapView = mapView;
|
||||
|
||||
String programCacheDir = context.getCacheDir().getAbsolutePath();
|
||||
nativeInitialize(this, fileSource, pixelRatio, programCacheDir);
|
||||
Context context = mapView.getContext();
|
||||
fileSource = FileSource.getInstance(context);
|
||||
pixelRatio = context.getResources().getDisplayMetrics().density;
|
||||
|
||||
nativeInitialize(this, fileSource, mapRenderer, pixelRatio);
|
||||
}
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
public void setRenderThread(RenderThread renderThread) {
|
||||
this.renderThread = renderThread;
|
||||
}
|
||||
|
||||
private boolean isDestroyedOn(String callingMethod) {
|
||||
if (destroyed && !TextUtils.isEmpty(callingMethod)) {
|
||||
Timber.e(
|
||||
@@ -113,14 +106,7 @@ final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
return;
|
||||
}
|
||||
|
||||
requestRender();
|
||||
}
|
||||
|
||||
public void render() {
|
||||
if (isDestroyedOn("render")) {
|
||||
return;
|
||||
}
|
||||
nativeRender();
|
||||
scheduler.requestRender();
|
||||
}
|
||||
|
||||
public void resizeView(int width, int height) {
|
||||
@@ -151,33 +137,10 @@ final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
+ "capping value at 65535 instead of %s", height);
|
||||
height = 65535;
|
||||
}
|
||||
|
||||
nativeResizeView(width, height);
|
||||
}
|
||||
|
||||
private void resizeFramebuffer(int fbWidth, int fbHeight) {
|
||||
if (isDestroyedOn("resizeFramebuffer")) {
|
||||
return;
|
||||
}
|
||||
if (fbWidth < 0) {
|
||||
throw new IllegalArgumentException("fbWidth cannot be negative.");
|
||||
}
|
||||
|
||||
if (fbHeight < 0) {
|
||||
throw new IllegalArgumentException("fbHeight cannot be negative.");
|
||||
}
|
||||
|
||||
if (fbWidth > 65535) {
|
||||
throw new IllegalArgumentException(
|
||||
"fbWidth cannot be greater than 65535.");
|
||||
}
|
||||
|
||||
if (fbHeight > 65535) {
|
||||
throw new IllegalArgumentException(
|
||||
"fbHeight cannot be greater than 65535.");
|
||||
}
|
||||
nativeResizeFramebuffer(fbWidth, fbHeight);
|
||||
}
|
||||
|
||||
public void setStyleUrl(String url) {
|
||||
if (isDestroyedOn("setStyleUrl")) {
|
||||
return;
|
||||
@@ -873,31 +836,6 @@ final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
// Callbacks
|
||||
//
|
||||
|
||||
/**
|
||||
* Called from JNI whenever the native map
|
||||
* needs rendering.
|
||||
*/
|
||||
protected void requestRender() {
|
||||
if (renderThread != null) {
|
||||
renderThread.requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from JNI when work needs to be processed on
|
||||
* the Renderer Thread.
|
||||
*/
|
||||
protected void requestProcessing() {
|
||||
if (renderThread != null) {
|
||||
renderThread.queueEvent(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nativeProcess();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void onMapChanged(int rawChange) {
|
||||
if (mapView != null) {
|
||||
mapView.onMapChange(rawChange);
|
||||
@@ -928,33 +866,13 @@ final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
|
||||
private native void nativeInitialize(NativeMapView nativeMapView,
|
||||
FileSource fileSource,
|
||||
float pixelRatio,
|
||||
String programCacheDir);
|
||||
MapRenderer mapRenderer,
|
||||
float pixelRatio);
|
||||
|
||||
private native void nativeDestroy();
|
||||
|
||||
private native void nativeProcess();
|
||||
|
||||
private native void nativeInitializeDisplay();
|
||||
|
||||
private native void nativeTerminateDisplay();
|
||||
|
||||
private native void nativeInitializeContext();
|
||||
|
||||
private native void nativeTerminateContext();
|
||||
|
||||
private native void nativeCreateSurface(Object surface);
|
||||
|
||||
private native void nativeDestroySurface();
|
||||
|
||||
private native void nativeUpdate();
|
||||
|
||||
private native void nativeRender();
|
||||
|
||||
private native void nativeResizeView(int width, int height);
|
||||
|
||||
private native void nativeResizeFramebuffer(int fbWidth, int fbHeight);
|
||||
|
||||
private native void nativeSetStyleUrl(String url);
|
||||
|
||||
private native String nativeGetStyleUrl();
|
||||
@@ -1160,30 +1078,7 @@ final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback) {
|
||||
snapshotReadyCallback = callback;
|
||||
scheduleTakeSnapshot();
|
||||
renderThread.requestRender();
|
||||
scheduler.requestRender();
|
||||
}
|
||||
|
||||
//
|
||||
// GLSurfaceView.Renderer
|
||||
//
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
Timber.i("[%s] onSurfaceCreated", Thread.currentThread().getName());
|
||||
//TODO: callback to map to re-create renderer?
|
||||
}
|
||||
|
||||
@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);
|
||||
resizeFramebuffer(width, height);
|
||||
// resizeView(width, height); Done from MapView#onSizeChanged
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
nativeRender();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.maps.renderer;
|
||||
|
||||
import android.opengl.GLSurfaceView;
|
||||
|
||||
/**
|
||||
* {@link RenderThread} implementation that schedules using the
|
||||
* {@link GLSurfaceView} thread.
|
||||
*/
|
||||
public class GlSurfaceViewRenderThread implements RenderThread {
|
||||
private final GLSurfaceView surfaceView;
|
||||
|
||||
public GlSurfaceViewRenderThread(GLSurfaceView surfaceView) {
|
||||
this.surfaceView = surfaceView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestRender() {
|
||||
surfaceView.requestRender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueEvent(Runnable runnable) {
|
||||
surfaceView.queueEvent(runnable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.mapbox.mapboxsdk.maps.renderer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
|
||||
import com.mapbox.mapboxsdk.storage.FileSource;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
/**
|
||||
* The {@link MapRenderer} encapsulates the GL thread.
|
||||
* <p>
|
||||
* Performs actions on the GL thread to manage the GL resources and
|
||||
* 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 {
|
||||
|
||||
// Holds the pointer to the native peer after initialisation
|
||||
private long nativePtr = 0;
|
||||
|
||||
private final GLSurfaceView glSurfaceView;
|
||||
|
||||
public MapRenderer(Context context, GLSurfaceView glSurfaceView) {
|
||||
this.glSurfaceView = glSurfaceView;
|
||||
|
||||
FileSource fileSource = FileSource.getInstance(context);
|
||||
float pixelRatio = context.getResources().getDisplayMetrics().density;
|
||||
String programCacheDir = context.getCacheDir().getAbsolutePath();
|
||||
|
||||
// Initialise native peer
|
||||
nativeInitialize(this, fileSource, pixelRatio, programCacheDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
nativeOnSurfaceCreated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||
if (width < 0) {
|
||||
throw new IllegalArgumentException("fbWidth cannot be negative.");
|
||||
}
|
||||
|
||||
if (height < 0) {
|
||||
throw new IllegalArgumentException("fbHeight cannot be negative.");
|
||||
}
|
||||
|
||||
if (width > 65535) {
|
||||
throw new IllegalArgumentException(
|
||||
"fbWidth cannot be greater than 65535.");
|
||||
}
|
||||
|
||||
if (height > 65535) {
|
||||
throw new IllegalArgumentException(
|
||||
"fbHeight cannot be greater than 65535.");
|
||||
}
|
||||
|
||||
gl.glViewport(0, 0, width, height);
|
||||
nativeOnSurfaceChanged(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
nativeRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* May be called from any thread.
|
||||
* <p>
|
||||
* Called from the renderer frontend to schedule a render.
|
||||
*/
|
||||
@Override
|
||||
public void requestRender() {
|
||||
glSurfaceView.requestRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* May be called from any thread.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Called from the native peer to schedule work on the GL
|
||||
* thread. Explicit override for easier to read jni code.
|
||||
*
|
||||
* @param runnable the runnable to execute
|
||||
* @see MapRendererRunnable
|
||||
*/
|
||||
void queueEvent(MapRendererRunnable runnable) {
|
||||
this.queueEvent((Runnable) runnable);
|
||||
}
|
||||
|
||||
private native void nativeInitialize(MapRenderer self,
|
||||
FileSource fileSource,
|
||||
float pixelRatio,
|
||||
String programCacheDir);
|
||||
|
||||
@Override
|
||||
protected native void finalize() throws Throwable;
|
||||
|
||||
private native void nativeOnSurfaceCreated();
|
||||
|
||||
private native void nativeOnSurfaceChanged(int width, int height);
|
||||
|
||||
private native void nativeRender();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mapbox.mapboxsdk.maps.renderer;
|
||||
|
||||
/**
|
||||
* Peer class for {@link Runnable}s to be scheduled on the {@link MapRenderer} thread.
|
||||
* The actual work is performed in the native peer.
|
||||
*/
|
||||
class MapRendererRunnable implements Runnable {
|
||||
|
||||
// Holds the pointer to the native peer after initialisation
|
||||
private final long nativePtr;
|
||||
|
||||
/**
|
||||
* Constructed from the native peer constructor
|
||||
*
|
||||
* @param nativePtr the native peer's memory address
|
||||
*/
|
||||
MapRendererRunnable(long nativePtr) {
|
||||
this.nativePtr = nativePtr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public native void run();
|
||||
|
||||
@Override
|
||||
protected native void finalize() throws Throwable;
|
||||
|
||||
private native void nativeInitialize();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mapbox.mapboxsdk.maps.renderer;
|
||||
|
||||
/**
|
||||
* Can be used to schedule work on the map renderer
|
||||
* thread or request a render.
|
||||
*/
|
||||
public interface MapRendererScheduler {
|
||||
|
||||
void requestRender();
|
||||
|
||||
void queueEvent(Runnable runnable);
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.maps.renderer;
|
||||
|
||||
/**
|
||||
* Created by ivo on 11/09/2017.
|
||||
*/
|
||||
|
||||
public interface RenderThread {
|
||||
|
||||
void requestRender();
|
||||
|
||||
void queueEvent(Runnable runnable);
|
||||
}
|
||||
Reference in New Issue
Block a user