[android] async rendering
- introduces GLSurfaceView - introduces Orchestration thread - renders on the gl thread
This commit is contained in:
committed by
Ivo van Dongen
parent
e780cdc29e
commit
347d7c19c0
@@ -0,0 +1,293 @@
|
||||
package com.mapbox.mapboxsdk.egl;
|
||||
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.microedition.khronos.egl.EGL10;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.egl.EGLDisplay;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import static com.mapbox.mapboxsdk.utils.Compare.compare;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_MASK_SIZE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_BUFFER_SIZE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_COLOR_BUFFER_TYPE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_RGB_BUFFER;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
|
||||
import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
|
||||
|
||||
/**
|
||||
* Selects the right EGLConfig needed for `mapbox-gl-native`
|
||||
*/
|
||||
public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
|
||||
|
||||
/**
|
||||
* Requires API level 17
|
||||
*
|
||||
* @see android.opengl.EGL14.EGL_CONFORMANT;
|
||||
*/
|
||||
@SuppressWarnings("JavadocReference")
|
||||
private static final int EGL_CONFORMANT = 0x3042;
|
||||
|
||||
/**
|
||||
* Requires API level 17
|
||||
*
|
||||
* @see android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
|
||||
*/
|
||||
@SuppressWarnings("JavadocReference")
|
||||
private static final int EGL_OPENGL_ES2_BIT = 0x0004;
|
||||
|
||||
@Override
|
||||
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
|
||||
int[] configAttribs = getConfigAttributes();
|
||||
|
||||
// Determine number of possible configurations
|
||||
int[] numConfigs = getNumberOfConfigurations(egl, display, configAttribs);
|
||||
if (numConfigs[0] < 1) {
|
||||
Timber.e("eglChooseConfig() returned no configs.");
|
||||
throw new EGLConfigException("eglChooseConfig() failed");
|
||||
}
|
||||
|
||||
// Get all possible configurations
|
||||
EGLConfig[] possibleConfigurations = getPossibleConfigurations(egl, display, configAttribs, numConfigs);
|
||||
|
||||
// Choose best match
|
||||
EGLConfig config = chooseBestMatchConfig(egl, display, possibleConfigurations);
|
||||
if (config == null) {
|
||||
Timber.e("No config chosen");
|
||||
throw new EGLConfigException("No config chosen");
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private int[] getNumberOfConfigurations(EGL10 egl, EGLDisplay display, int[] configAttributes) {
|
||||
int[] numConfigs = new int[1];
|
||||
if (!egl.eglChooseConfig(display, configAttributes, null, 0, numConfigs)) {
|
||||
Timber.e("eglChooseConfig(NULL) returned error %d", egl.eglGetError());
|
||||
throw new EGLConfigException("eglChooseConfig() failed");
|
||||
}
|
||||
return numConfigs;
|
||||
}
|
||||
|
||||
private EGLConfig[] getPossibleConfigurations(EGL10 egl, EGLDisplay display,
|
||||
int[] configAttributes, int[] numConfigs) {
|
||||
EGLConfig[] configs = new EGLConfig[numConfigs[0]];
|
||||
if (!egl.eglChooseConfig(display, configAttributes, configs, numConfigs[0], numConfigs)) {
|
||||
Timber.e("eglChooseConfig() returned error %d", egl.eglGetError());
|
||||
throw new EGLConfigException("eglChooseConfig() failed");
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
// Quality
|
||||
enum BufferFormat {
|
||||
Format16Bit(3),
|
||||
Format32BitNoAlpha(1),
|
||||
Format32BitAlpha(2),
|
||||
Format24Bit(0),
|
||||
Unknown(4);
|
||||
|
||||
int value;
|
||||
|
||||
BufferFormat(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
enum DepthStencilFormat {
|
||||
Format16Depth8Stencil(1),
|
||||
Format24Depth8Stencil(0);
|
||||
|
||||
int value;
|
||||
|
||||
DepthStencilFormat(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private EGLConfig chooseBestMatchConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
|
||||
class Config implements Comparable<Config> {
|
||||
private final BufferFormat bufferFormat;
|
||||
private final DepthStencilFormat depthStencilFormat;
|
||||
private final boolean isNotConformant;
|
||||
private final boolean isCaveat;
|
||||
private final int index;
|
||||
private final EGLConfig config;
|
||||
|
||||
public Config(BufferFormat bufferFormat, DepthStencilFormat depthStencilFormat,
|
||||
boolean isNotConformant, boolean isCaveat, int index, EGLConfig config) {
|
||||
this.bufferFormat = bufferFormat;
|
||||
this.depthStencilFormat = depthStencilFormat;
|
||||
this.isNotConformant = isNotConformant;
|
||||
this.isCaveat = isCaveat;
|
||||
this.index = index;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull Config other) {
|
||||
int i = compare(bufferFormat.value, other.bufferFormat.value);
|
||||
if (i != 0) {
|
||||
return i;
|
||||
}
|
||||
|
||||
i = compare(depthStencilFormat.value, other.depthStencilFormat.value);
|
||||
if (i != 0) {
|
||||
return i;
|
||||
}
|
||||
|
||||
i = compare(isNotConformant, other.isNotConformant);
|
||||
if (i != 0) {
|
||||
return i;
|
||||
}
|
||||
|
||||
i = compare(isCaveat, other.isCaveat);
|
||||
if (i != 0) {
|
||||
return i;
|
||||
}
|
||||
|
||||
i = compare(index, other.index);
|
||||
if (i != 0) {
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
List<Config> matches = new ArrayList<>();
|
||||
|
||||
int i = 0;
|
||||
for (EGLConfig config : configs) {
|
||||
i++;
|
||||
|
||||
int caveat = getConfigAttr(egl, display, config, EGL_CONFIG_CAVEAT);
|
||||
int conformant = getConfigAttr(egl, display, config, EGL_CONFORMANT);
|
||||
int bits = getConfigAttr(egl, display, config, EGL_BUFFER_SIZE);
|
||||
int red = getConfigAttr(egl, display, config, EGL_RED_SIZE);
|
||||
int green = getConfigAttr(egl, display, config, EGL_GREEN_SIZE);
|
||||
int blue = getConfigAttr(egl, display, config, EGL_BLUE_SIZE);
|
||||
int alpha = getConfigAttr(egl, display, config, EGL_ALPHA_SIZE);
|
||||
int alphaMask = getConfigAttr(egl, display, config, EGL_ALPHA_MASK_SIZE);
|
||||
int depth = getConfigAttr(egl, display, config, EGL_DEPTH_SIZE);
|
||||
int stencil = getConfigAttr(egl, display, config, EGL_STENCIL_SIZE);
|
||||
int sampleBuffers = getConfigAttr(egl, display, config, EGL_SAMPLE_BUFFERS);
|
||||
int samples = getConfigAttr(egl, display, config, EGL_SAMPLES);
|
||||
|
||||
boolean 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 = BufferFormat.Format16Bit;
|
||||
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
|
||||
bufferFormat = BufferFormat.Format32BitNoAlpha;
|
||||
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
|
||||
bufferFormat = BufferFormat.Format32BitAlpha;
|
||||
} else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
|
||||
bufferFormat = BufferFormat.Format24Bit;
|
||||
} else {
|
||||
bufferFormat = BufferFormat.Unknown;
|
||||
}
|
||||
|
||||
// Work out the config's depth stencil format
|
||||
DepthStencilFormat depthStencilFormat;
|
||||
if ((depth == 16) && (stencil == 8)) {
|
||||
depthStencilFormat = DepthStencilFormat.Format16Depth8Stencil;
|
||||
} else {
|
||||
depthStencilFormat = DepthStencilFormat.Format24Depth8Stencil;
|
||||
}
|
||||
|
||||
boolean isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
|
||||
boolean isCaveat = caveat != EGL_NONE;
|
||||
|
||||
// Ignore formats we don't recognise
|
||||
if (bufferFormat != BufferFormat.Unknown) {
|
||||
matches.add(new Config(bufferFormat, depthStencilFormat, isNotConformant, isCaveat, i, config));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Sort
|
||||
Collections.sort(matches);
|
||||
|
||||
if (matches.size() == 0) {
|
||||
throw new EGLConfigException("No matching configurations after filtering");
|
||||
}
|
||||
|
||||
Config bestMatch = matches.get(0);
|
||||
|
||||
if (bestMatch.isCaveat) {
|
||||
Timber.w("Chosen config has a caveat.");
|
||||
}
|
||||
|
||||
if (bestMatch.isNotConformant) {
|
||||
Timber.w("Chosen config is not conformant.");
|
||||
}
|
||||
|
||||
return bestMatch.config;
|
||||
}
|
||||
|
||||
private int getConfigAttr(EGL10 egl, EGLDisplay display, EGLConfig config, int attributeName) {
|
||||
int[] attributevalue = new int[1];
|
||||
if (!egl.eglGetConfigAttrib(display, config, attributeName, attributevalue)) {
|
||||
Timber.e("eglGetConfigAttrib(%d) returned error %d", attributeName, egl.eglGetError());
|
||||
throw new EGLConfigException("eglGetConfigAttrib() failed");
|
||||
}
|
||||
return attributevalue[0];
|
||||
}
|
||||
|
||||
|
||||
private int[] getConfigAttributes() {
|
||||
boolean emulator = inEmulator();
|
||||
Timber.i("In emulator: %s", emulator);
|
||||
|
||||
// Get all configs at least RGB 565 with 16 depth and 8 stencil
|
||||
return new int[] {
|
||||
EGL_CONFIG_CAVEAT, EGL_NONE,
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_BUFFER_SIZE, 16,
|
||||
EGL_RED_SIZE, 5,
|
||||
EGL_GREEN_SIZE, 6,
|
||||
EGL_BLUE_SIZE, 5,
|
||||
EGL_ALPHA_SIZE, 0,
|
||||
EGL_DEPTH_SIZE, 16,
|
||||
EGL_STENCIL_SIZE, 8,
|
||||
(emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT,
|
||||
(emulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if we are in emulator.
|
||||
*/
|
||||
private boolean inEmulator() {
|
||||
return System.getProperty("ro.kernel.qemu") != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mapbox.mapboxsdk.egl;
|
||||
|
||||
/**
|
||||
* Used for EGL configuration exceptions
|
||||
*/
|
||||
public class EGLConfigException extends RuntimeException {
|
||||
public EGLConfigException() {
|
||||
}
|
||||
|
||||
public EGLConfigException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public EGLConfigException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public EGLConfigException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.mapbox.mapboxsdk.maps;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Build;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.IntDef;
|
||||
@@ -16,10 +15,6 @@ import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
@@ -32,6 +27,7 @@ import com.mapbox.mapboxsdk.annotations.Annotation;
|
||||
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.widgets.CompassView;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
|
||||
@@ -45,10 +41,14 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION;
|
||||
import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE;
|
||||
import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -72,7 +72,6 @@ public class MapView extends FrameLayout {
|
||||
private NativeMapView nativeMapView;
|
||||
private MapboxMapOptions mapboxMapOptions;
|
||||
private boolean destroyed;
|
||||
private boolean hasSurface;
|
||||
|
||||
private MyLocationView myLocationView;
|
||||
private CompassView compassView;
|
||||
@@ -86,6 +85,8 @@ public class MapView extends FrameLayout {
|
||||
private Bundle savedInstanceState;
|
||||
private final CopyOnWriteArrayList<OnMapChangedListener> onMapChangedListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
private GLSurfaceView glSurfaceView;
|
||||
|
||||
@UiThread
|
||||
public MapView(@NonNull Context context) {
|
||||
super(context);
|
||||
@@ -280,10 +281,37 @@ public class MapView extends FrameLayout {
|
||||
}
|
||||
|
||||
private void initialiseDrawingSurface() {
|
||||
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
|
||||
surfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
|
||||
surfaceView.getHolder().addCallback(new SurfaceCallback());
|
||||
surfaceView.setVisibility(View.VISIBLE);
|
||||
glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView);
|
||||
glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
|
||||
glSurfaceView.setEGLContextClientVersion(2);
|
||||
glSurfaceView.setEGLConfigChooser(new EGLConfigChooser());
|
||||
glSurfaceView.setRenderer(new GLSurfaceView.Renderer() {
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
MapView.this.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
initialiseMap();
|
||||
mapboxMap.onStart();
|
||||
}
|
||||
});
|
||||
|
||||
nativeMapView.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.setRenderMode(RENDERMODE_WHEN_DIRTY);
|
||||
glSurfaceView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,7 +342,9 @@ public class MapView extends FrameLayout {
|
||||
*/
|
||||
@UiThread
|
||||
public void onResume() {
|
||||
// replaced by onStart in v5.0.0
|
||||
if (glSurfaceView != null) {
|
||||
glSurfaceView.onResume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,7 +352,9 @@ public class MapView extends FrameLayout {
|
||||
*/
|
||||
@UiThread
|
||||
public void onPause() {
|
||||
// replaced by onStop in v5.0.0
|
||||
if (glSurfaceView != null) {
|
||||
glSurfaceView.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,24 +497,10 @@ public class MapView extends FrameLayout {
|
||||
// Called when the map needs to be rerendered
|
||||
// Called via JNI from NativeMapView
|
||||
protected void onInvalidate() {
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (isInEditMode()) {
|
||||
return;
|
||||
if (glSurfaceView != null) {
|
||||
glSurfaceView.requestRender();
|
||||
}
|
||||
|
||||
if (destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasSurface) {
|
||||
return;
|
||||
}
|
||||
nativeMapView.render();
|
||||
// TODO: removable? postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -496,103 +514,6 @@ public class MapView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private class SurfaceCallback implements SurfaceHolder.Callback {
|
||||
|
||||
private Surface surface;
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
if (nativeMapView == null) {
|
||||
nativeMapView = new NativeMapView(MapView.this);
|
||||
nativeMapView.createSurface(surface = holder.getSurface());
|
||||
nativeMapView.resizeView(getWidth(), getHeight());
|
||||
initialiseMap();
|
||||
mapboxMap.onStart();
|
||||
} else {
|
||||
nativeMapView.createSurface(surface = holder.getSurface());
|
||||
}
|
||||
|
||||
hasSurface = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
if (destroyed) {
|
||||
return;
|
||||
}
|
||||
nativeMapView.resizeFramebuffer(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
hasSurface = false;
|
||||
|
||||
if (nativeMapView != null) {
|
||||
// occurs when activity goes to background
|
||||
nativeMapView.destroySurface();
|
||||
}
|
||||
surface.release();
|
||||
}
|
||||
}
|
||||
|
||||
// This class handles TextureView callbacks
|
||||
private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
|
||||
|
||||
private Surface surface;
|
||||
|
||||
// Called when the native surface texture has been created
|
||||
// Must do all EGL/GL ES initialization here
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
||||
if (nativeMapView == null) {
|
||||
nativeMapView = new NativeMapView(MapView.this);
|
||||
nativeMapView.createSurface(this.surface = new Surface(surface));
|
||||
nativeMapView.resizeFramebuffer(width, height);
|
||||
nativeMapView.resizeView(width, height);
|
||||
initialiseMap();
|
||||
mapboxMap.onStart();
|
||||
} else {
|
||||
nativeMapView.createSurface(this.surface = new Surface(surface));
|
||||
}
|
||||
|
||||
hasSurface = true;
|
||||
}
|
||||
|
||||
// Called when the native surface texture has been destroyed
|
||||
// Must do all EGL/GL ES destruction here
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
||||
hasSurface = false;
|
||||
|
||||
if (nativeMapView != null) {
|
||||
nativeMapView.destroySurface();
|
||||
}
|
||||
this.surface.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called when the format or size of the native surface texture has been changed
|
||||
// Must handle window resizing here.
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
|
||||
if (destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
nativeMapView.resizeFramebuffer(width, height);
|
||||
}
|
||||
|
||||
// Called when the SurfaceTexure frame is drawn to screen
|
||||
// Must sync with UI here
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
|
||||
if (destroyed) {
|
||||
return;
|
||||
}
|
||||
mapboxMap.onUpdateRegionChange();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// View events
|
||||
//
|
||||
@@ -612,7 +533,9 @@ public class MapView extends FrameLayout {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nativeMapView != null) {
|
||||
if (nativeMapView == null) {
|
||||
nativeMapView = new NativeMapView(this);
|
||||
} else if (mapZoomButtonController != null) {
|
||||
mapZoomButtonController.setVisible(visibility == View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ 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;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.mapbox.mapboxsdk.LibraryLoader;
|
||||
import com.mapbox.mapboxsdk.annotations.Icon;
|
||||
@@ -35,10 +35,13 @@ 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 {
|
||||
final class NativeMapView implements GLSurfaceView.Renderer {
|
||||
|
||||
// Flag to indicating destroy was called
|
||||
private boolean destroyed = false;
|
||||
@@ -97,20 +100,6 @@ final class NativeMapView {
|
||||
destroyed = true;
|
||||
}
|
||||
|
||||
public void createSurface(Surface surface) {
|
||||
if (isDestroyedOn("createSurface")) {
|
||||
return;
|
||||
}
|
||||
nativeCreateSurface(surface);
|
||||
}
|
||||
|
||||
public void destroySurface() {
|
||||
if (isDestroyedOn("destroySurface")) {
|
||||
return;
|
||||
}
|
||||
nativeDestroySurface();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (isDestroyedOn("update")) {
|
||||
return;
|
||||
@@ -156,7 +145,7 @@ final class NativeMapView {
|
||||
nativeResizeView(width, height);
|
||||
}
|
||||
|
||||
public void resizeFramebuffer(int fbWidth, int fbHeight) {
|
||||
private void resizeFramebuffer(int fbWidth, int fbHeight) {
|
||||
if (isDestroyedOn("resizeFramebuffer")) {
|
||||
return;
|
||||
}
|
||||
@@ -1141,6 +1130,32 @@ final class NativeMapView {
|
||||
void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback) {
|
||||
snapshotReadyCallback = callback;
|
||||
scheduleTakeSnapshot();
|
||||
render();
|
||||
mapView.onInvalidate();
|
||||
// TODO. this should do a request render
|
||||
//render();
|
||||
}
|
||||
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.mapbox.mapboxsdk.utils;
|
||||
|
||||
/**
|
||||
* Comparisons from std sdk, which aren't available in API level <= 15
|
||||
*/
|
||||
public class Compare {
|
||||
|
||||
/**
|
||||
* @see Integer#compare(int, int)
|
||||
* @param x left side
|
||||
* @param y right side
|
||||
* @return std compare value
|
||||
*/
|
||||
public static int compare(int x, int y) {
|
||||
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Boolean#compare(boolean, boolean)
|
||||
* @param x left side
|
||||
* @param y right side
|
||||
* @return std compare value
|
||||
*/
|
||||
public static int compare(boolean x, boolean y) {
|
||||
return (x == y) ? 0 : (x ? 1 : -1);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<SurfaceView
|
||||
<android.opengl.GLSurfaceView
|
||||
android:id="@+id/surfaceView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
76
platform/android/src/android_gl_thread.hpp
Normal file
76
platform/android/src/android_gl_thread.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/actor/actor.hpp>
|
||||
#include <mbgl/actor/mailbox.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <mbgl/renderer/renderer.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class AndroidGLThread : public Scheduler {
|
||||
public:
|
||||
using InvalidateCallback = std::function<void ()>;
|
||||
|
||||
template <class... Args>
|
||||
AndroidGLThread(InvalidateCallback callback_, Args&&... args)
|
||||
: renderer(std::make_unique<Renderer>(std::forward<Args>(args)...))
|
||||
, mailbox(std::make_shared<Mailbox>(*this))
|
||||
, callback(callback_)
|
||||
, rendererRef(*renderer, mailbox) {
|
||||
}
|
||||
|
||||
~AndroidGLThread() override = default;
|
||||
|
||||
ActorRef<Renderer> actor() const {
|
||||
return rendererRef;
|
||||
}
|
||||
|
||||
void schedule(std::weak_ptr<Mailbox> scheduled) override {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
queue.push(scheduled);
|
||||
}
|
||||
|
||||
// Invalidate so we get processing time later
|
||||
callback();
|
||||
}
|
||||
|
||||
// Only safe on the GL Thread
|
||||
void process() {
|
||||
while (true) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
if (queue.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto scheduled = queue.front();
|
||||
queue.pop();
|
||||
|
||||
lock.unlock();
|
||||
|
||||
Mailbox::maybeReceive(scheduled);
|
||||
}
|
||||
}
|
||||
|
||||
// Only safe to access on the GL Thread
|
||||
std::unique_ptr<Renderer> renderer;
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::queue<std::weak_ptr<Mailbox>> queue;
|
||||
|
||||
std::shared_ptr<Mailbox> mailbox;
|
||||
InvalidateCallback callback;
|
||||
ActorRef<Renderer> rendererRef;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -1,72 +1,188 @@
|
||||
#include "android_renderer_frontend.hpp"
|
||||
|
||||
#include <mbgl/renderer/backend_scope.hpp>
|
||||
#include <mbgl/renderer/renderer.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <mbgl/renderer/renderer_observer.hpp>
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
#include <mbgl/util/thread.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
#include "android_renderer_backend.hpp"
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
AndroidRendererFrontend::AndroidRendererFrontend(
|
||||
std::unique_ptr<Renderer> renderer_,
|
||||
RendererBackend& backend_,
|
||||
InvalidateCallback invalidate)
|
||||
: renderer(std::move(renderer_))
|
||||
, backend(backend_)
|
||||
, asyncInvalidate([=, invalidate=std::move(invalidate)]() {
|
||||
// Forwards RendererObserver signals to the given
|
||||
// Delegate RendererObserver on the given RunLoop
|
||||
class ForwardingRendererObserver : public RendererObserver {
|
||||
public:
|
||||
ForwardingRendererObserver(util::RunLoop& mapRunLoop, RendererObserver& delegate_)
|
||||
: mailbox(std::make_shared<Mailbox>(mapRunLoop))
|
||||
, delegate(delegate_, mailbox) {
|
||||
}
|
||||
|
||||
~ForwardingRendererObserver() {
|
||||
mailbox->close();
|
||||
}
|
||||
|
||||
void onInvalidate() override {
|
||||
delegate.invoke(&RendererObserver::onInvalidate);
|
||||
}
|
||||
|
||||
void onResourceError(std::exception_ptr err) override {
|
||||
delegate.invoke(&RendererObserver::onResourceError, err);
|
||||
}
|
||||
|
||||
void onWillStartRenderingMap() override {
|
||||
delegate.invoke(&RendererObserver::onWillStartRenderingMap);
|
||||
}
|
||||
|
||||
void onWillStartRenderingFrame() override {
|
||||
delegate.invoke(&RendererObserver::onWillStartRenderingFrame);
|
||||
}
|
||||
|
||||
void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) override {
|
||||
delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded);
|
||||
}
|
||||
|
||||
void onDidFinishRenderingMap() override {
|
||||
delegate.invoke(&RendererObserver::onDidFinishRenderingMap);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Mailbox> mailbox;
|
||||
ActorRef<RendererObserver> delegate;
|
||||
};
|
||||
|
||||
AndroidRendererFrontend::AndroidRendererFrontend(float pixelRatio,
|
||||
FileSource& fileSource,
|
||||
Scheduler& scheduler,
|
||||
std::string programCacheDir,
|
||||
InvalidateCallback invalidate)
|
||||
: backend(std::make_unique<AndroidRendererBackend>())
|
||||
, glThread(std::make_unique<AndroidGLThread>(
|
||||
invalidate,
|
||||
*backend,
|
||||
pixelRatio,
|
||||
fileSource,
|
||||
scheduler,
|
||||
GLContextMode::Unique,
|
||||
programCacheDir
|
||||
))
|
||||
, asyncInvalidate([=]() {
|
||||
invalidate();
|
||||
}) {
|
||||
})
|
||||
, mapRunLoop(util::RunLoop::Get()) {
|
||||
}
|
||||
|
||||
AndroidRendererFrontend::~AndroidRendererFrontend() = default;
|
||||
|
||||
void AndroidRendererFrontend::reset() {
|
||||
assert (renderer);
|
||||
if (renderer) {
|
||||
renderer.reset();
|
||||
}
|
||||
assert(glThread);
|
||||
glThread.reset();
|
||||
}
|
||||
|
||||
void AndroidRendererFrontend::setObserver(RendererObserver& observer) {
|
||||
assert (renderer);
|
||||
renderer->setObserver(&observer);
|
||||
assert (glThread);
|
||||
assert (util::RunLoop::Get());
|
||||
rendererObserver = std::make_unique<ForwardingRendererObserver>(*mapRunLoop, observer);
|
||||
glThread->actor().invoke(&Renderer::setObserver, rendererObserver.get());
|
||||
}
|
||||
|
||||
void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) {
|
||||
updateParameters = std::move(params);
|
||||
{
|
||||
// Lock on the parameters
|
||||
std::lock_guard<std::mutex> lock(updateMutex);
|
||||
updateParameters = std::move(params);
|
||||
}
|
||||
asyncInvalidate.send();
|
||||
}
|
||||
|
||||
// Called on OpenGL thread
|
||||
void AndroidRendererFrontend::render() {
|
||||
assert (renderer);
|
||||
if (!updateParameters) return;
|
||||
assert (glThread);
|
||||
|
||||
BackendScope guard { backend };
|
||||
std::shared_ptr<UpdateParameters> params;
|
||||
{
|
||||
// Lock on the parameters
|
||||
std::unique_lock<std::mutex> lock(updateMutex);
|
||||
if (!updateParameters) return;
|
||||
|
||||
renderer->render(*updateParameters);
|
||||
// Hold on to the update parameters during render
|
||||
params = updateParameters;
|
||||
}
|
||||
|
||||
// Process the gl thread mailbox
|
||||
glThread->process();
|
||||
|
||||
// Activate the backend
|
||||
BackendScope backendGuard { *backend };
|
||||
|
||||
// Ensure that the "current" scheduler on the render thread is
|
||||
// actually the scheduler from the orchestration thread
|
||||
Scheduler::SetCurrent(glThread.get());
|
||||
|
||||
if (framebufferSizeChanged) {
|
||||
backend->updateViewPort();
|
||||
framebufferSizeChanged = false;
|
||||
}
|
||||
|
||||
glThread->renderer->render(*params);
|
||||
}
|
||||
|
||||
void AndroidRendererFrontend::onLowMemory() {
|
||||
assert (renderer);
|
||||
renderer->onLowMemory();
|
||||
assert (glThread);
|
||||
glThread->actor().invoke(&Renderer::onLowMemory);
|
||||
}
|
||||
|
||||
std::vector<Feature> AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID,
|
||||
const SourceQueryOptions& options) const {
|
||||
return renderer->querySourceFeatures(sourceID, options);
|
||||
assert (glThread);
|
||||
// Waits for the result from the orchestration thread and returns
|
||||
return glThread->actor().ask(&Renderer::querySourceFeatures, sourceID, options).get();
|
||||
}
|
||||
|
||||
std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenBox& box,
|
||||
const RenderedQueryOptions& options) const {
|
||||
return renderer->queryRenderedFeatures(box, options);
|
||||
assert (glThread);
|
||||
|
||||
// Select the right overloaded method
|
||||
std::vector<Feature> (Renderer::*fn)(const ScreenBox&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures;
|
||||
|
||||
// Waits for the result from the orchestration thread and returns
|
||||
return glThread->actor().ask(fn, box, options).get();
|
||||
}
|
||||
|
||||
std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenCoordinate& point,
|
||||
const RenderedQueryOptions& options) const {
|
||||
return renderer->queryRenderedFeatures(point, options);
|
||||
assert (glThread);
|
||||
|
||||
// Select the right overloaded method
|
||||
std::vector<Feature> (Renderer::*fn)(const ScreenCoordinate&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures;
|
||||
|
||||
// Waits for the result from the orchestration thread and returns
|
||||
return glThread->actor().ask(fn, point, options).get();
|
||||
}
|
||||
|
||||
AnnotationIDs AndroidRendererFrontend::queryPointAnnotations(const ScreenBox& box) const {
|
||||
return renderer->queryPointAnnotations(box);
|
||||
assert (glThread);
|
||||
|
||||
// Waits for the result from the orchestration thread and returns
|
||||
return glThread->actor().ask(&Renderer::queryPointAnnotations, box).get();
|
||||
}
|
||||
|
||||
void AndroidRendererFrontend::requestSnapshot(SnapshotCallback callback_) {
|
||||
snapshotCallback = std::make_unique<SnapshotCallback>([callback=std::move(callback_), runloop=util::RunLoop::Get()](PremultipliedImage image){
|
||||
runloop->invoke([&]() {
|
||||
callback(std::move(image));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void AndroidRendererFrontend::resizeFramebuffer(int width, int height) {
|
||||
backend->resizeFramebuffer(width, height);
|
||||
framebufferSizeChanged = true;
|
||||
asyncInvalidate.send();
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -1,34 +1,50 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "android_gl_thread.hpp"
|
||||
|
||||
#include <mbgl/actor/actor.hpp>
|
||||
#include <mbgl/annotation/annotation.hpp>
|
||||
#include <mbgl/renderer/renderer_backend.hpp>
|
||||
#include <mbgl/renderer/renderer_frontend.hpp>
|
||||
#include <mbgl/util/async_task.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Renderer;
|
||||
class FileSource;
|
||||
class Scheduler;
|
||||
class RenderedQueryOptions;
|
||||
class SourceQueryOptions;
|
||||
|
||||
namespace android {
|
||||
|
||||
class AndroidRendererBackend;
|
||||
|
||||
class AndroidRendererFrontend : public RendererFrontend {
|
||||
public:
|
||||
using InvalidateCallback = std::function<void ()>;
|
||||
AndroidRendererFrontend(std::unique_ptr<Renderer>, RendererBackend&, InvalidateCallback);
|
||||
|
||||
AndroidRendererFrontend(float pixelRatio,
|
||||
mbgl::FileSource&,
|
||||
mbgl::Scheduler&,
|
||||
std::string programCacheDir,
|
||||
InvalidateCallback);
|
||||
~AndroidRendererFrontend() override;
|
||||
|
||||
void reset() override;
|
||||
void setObserver(RendererObserver&) override;
|
||||
|
||||
void update(std::shared_ptr<UpdateParameters>) override;
|
||||
|
||||
// Called from OpenGL Thread
|
||||
void render();
|
||||
|
||||
// Feature querying
|
||||
@@ -37,14 +53,31 @@ public:
|
||||
std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const;
|
||||
AnnotationIDs queryPointAnnotations(const ScreenBox& box) const;
|
||||
|
||||
// RenderBackend proxy - Called from OpenGL Thread
|
||||
void resizeFramebuffer(int width, int height);
|
||||
|
||||
// Memory
|
||||
void onLowMemory();
|
||||
|
||||
// Snapshot - Callback will be called on calling thread through RunLoop
|
||||
using SnapshotCallback = std::function<void (PremultipliedImage)>;
|
||||
void requestSnapshot(SnapshotCallback);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Renderer> renderer;
|
||||
RendererBackend& backend;
|
||||
std::unique_ptr<AndroidRendererBackend> backend;
|
||||
std::unique_ptr<AndroidGLThread> glThread;
|
||||
std::unique_ptr<RendererObserver> rendererObserver;
|
||||
|
||||
std::mutex updateMutex;
|
||||
std::shared_ptr<UpdateParameters> updateParameters;
|
||||
|
||||
|
||||
util::AsyncTask asyncInvalidate;
|
||||
|
||||
util::RunLoop* mapRunLoop;
|
||||
|
||||
bool framebufferSizeChanged = true;
|
||||
std::unique_ptr<SnapshotCallback> snapshotCallback;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -61,8 +61,7 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env,
|
||||
jni::Object<FileSource> jFileSource,
|
||||
jni::jfloat _pixelRatio,
|
||||
jni::String _programCacheDir)
|
||||
: rendererBackend(std::make_unique<AndroidRendererBackend>()),
|
||||
javaPeer(_obj.NewWeakGlobalRef(_env)),
|
||||
: javaPeer(_obj.NewWeakGlobalRef(_env)),
|
||||
pixelRatio(_pixelRatio),
|
||||
threadPool(sharedThreadPool()) {
|
||||
|
||||
@@ -72,16 +71,13 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env,
|
||||
return;
|
||||
}
|
||||
|
||||
auto& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource);
|
||||
|
||||
// Create a renderer
|
||||
auto renderer = std::make_unique<Renderer>(*rendererBackend, pixelRatio, fileSource,
|
||||
*threadPool, GLContextMode::Unique,
|
||||
jni::Make<std::string>(_env, _programCacheDir));
|
||||
mbgl::FileSource& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource);
|
||||
|
||||
// Create a renderer frontend
|
||||
rendererFrontend = std::make_unique<AndroidRendererFrontend>(std::move(renderer),
|
||||
*rendererBackend,
|
||||
rendererFrontend = std::make_unique<AndroidRendererFrontend>(pixelRatio, fileSource,
|
||||
*threadPool,
|
||||
jni::Make<std::string>(_env,
|
||||
_programCacheDir),
|
||||
[this] { this->invalidate(); });
|
||||
|
||||
// Create the core map
|
||||
@@ -90,29 +86,21 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env,
|
||||
static_cast<uint32_t>(height) }, pixelRatio,
|
||||
fileSource, *threadPool, MapMode::Continuous,
|
||||
ConstrainMode::HeightOnly, ViewportMode::Default);
|
||||
|
||||
// initialize egl components
|
||||
_initializeDisplay();
|
||||
_initializeContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called through NativeMapView#destroy()
|
||||
*/
|
||||
NativeMapView::~NativeMapView() {
|
||||
_terminateContext();
|
||||
_destroySurface();
|
||||
_terminateDisplay();
|
||||
|
||||
map.reset();
|
||||
|
||||
vm = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to java NativeMapView#onInvalidate().
|
||||
*
|
||||
* May be called from any thread
|
||||
* Called to invalidate the View and schedule a render on the next
|
||||
* runloop iteration.
|
||||
*/
|
||||
void NativeMapView::invalidate() {
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
@@ -199,48 +187,12 @@ void NativeMapView::onSourceChanged(mbgl::style::Source&) {
|
||||
|
||||
// JNI Methods //
|
||||
|
||||
void NativeMapView::createSurface(jni::JNIEnv& env, jni::Object<> _surface) {
|
||||
_createSurface(ANativeWindow_fromSurface(&env, jni::Unwrap(*_surface)));
|
||||
}
|
||||
|
||||
void NativeMapView::destroySurface(jni::JNIEnv&) {
|
||||
_destroySurface();
|
||||
}
|
||||
|
||||
void NativeMapView::render(jni::JNIEnv& env) {
|
||||
BackendScope guard { *rendererBackend };
|
||||
|
||||
if (framebufferSizeChanged) {
|
||||
rendererBackend->updateViewPort();
|
||||
framebufferSizeChanged = false;
|
||||
}
|
||||
|
||||
// Called from the OpenGL renderer thread
|
||||
void NativeMapView::render(jni::JNIEnv& ) {
|
||||
rendererFrontend->render();
|
||||
|
||||
if(snapshot){
|
||||
snapshot = false;
|
||||
|
||||
// take snapshot
|
||||
auto image = rendererBackend->readFramebuffer();
|
||||
auto bitmap = Bitmap::CreateBitmap(env, std::move(image));
|
||||
|
||||
// invoke Mapview#OnSnapshotReady
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
|
||||
javaPeer->Call(*_env, onSnapshotReady, bitmap);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
// TODO
|
||||
updateFps();
|
||||
}
|
||||
|
||||
void NativeMapView::update(jni::JNIEnv&) {
|
||||
@@ -254,9 +206,9 @@ void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) {
|
||||
}
|
||||
|
||||
void NativeMapView::resizeFramebuffer(jni::JNIEnv&, int w, int h) {
|
||||
rendererBackend->resizeFramebuffer(w, h);
|
||||
framebufferSizeChanged = true;
|
||||
invalidate();
|
||||
rendererFrontend->resizeFramebuffer(w, h);
|
||||
// framebufferSizeChanged = true;
|
||||
// invalidate();
|
||||
}
|
||||
|
||||
jni::String NativeMapView::getStyleUrl(jni::JNIEnv& env) {
|
||||
@@ -476,7 +428,15 @@ void NativeMapView::setContentPadding(JNIEnv&, double top, double left, double b
|
||||
}
|
||||
|
||||
void NativeMapView::scheduleSnapshot(jni::JNIEnv&) {
|
||||
snapshot = true;
|
||||
rendererFrontend->requestSnapshot([&](PremultipliedImage image) {
|
||||
auto _env = android::AttachEnv();
|
||||
// Convert image to bitmap
|
||||
auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image));
|
||||
|
||||
// invoke Mapview#OnSnapshotReady
|
||||
static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
|
||||
javaPeer->Call(*_env, onSnapshotReady, bitmap);
|
||||
});
|
||||
}
|
||||
|
||||
void NativeMapView::enableFps(jni::JNIEnv&, jni::jboolean enable) {
|
||||
@@ -1001,371 +961,6 @@ jni::jboolean NativeMapView::getPrefetchesTiles(JNIEnv&) {
|
||||
|
||||
// Private methods //
|
||||
|
||||
void NativeMapView::_initializeDisplay() {
|
||||
assert(display == EGL_NO_DISPLAY);
|
||||
assert(config == nullptr);
|
||||
assert(format < 0);
|
||||
|
||||
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
// 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::_terminateDisplay() {
|
||||
if (display != EGL_NO_DISPLAY) {
|
||||
// Destroy the surface first, if it still exists. This call needs a valid surface.
|
||||
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 (!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);
|
||||
|
||||
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
||||
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
|
||||
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");
|
||||
}
|
||||
|
||||
if (context != EGL_NO_CONTEXT) {
|
||||
if (!eglDestroyContext(display, context)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d",
|
||||
eglGetError());
|
||||
throw std::runtime_error("eglDestroyContext() 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);
|
||||
|
||||
const EGLint surfaceAttribs[] = {EGL_NONE};
|
||||
surface = eglCreateWindowSurface(display, config, window, surfaceAttribs);
|
||||
if (surface == EGL_NO_SURFACE) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d",
|
||||
eglGetError());
|
||||
throw std::runtime_error("eglCreateWindowSurface() failed");
|
||||
}
|
||||
|
||||
if (firstRender) {
|
||||
firstRender = false;
|
||||
|
||||
BackendScope guard(*rendererBackend);
|
||||
|
||||
if (!eglMakeCurrent(display, surface, surface, context)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
|
||||
eglGetError());
|
||||
throw std::runtime_error("eglMakeCurrent() failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
firstRender = true;
|
||||
|
||||
if (window != nullptr) {
|
||||
ANativeWindow_release(window);
|
||||
window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::updateFps() {
|
||||
if (!fpsEnabled) {
|
||||
return;
|
||||
@@ -1412,8 +1007,6 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
|
||||
METHOD(&NativeMapView::update, "nativeUpdate"),
|
||||
METHOD(&NativeMapView::resizeView, "nativeResizeView"),
|
||||
METHOD(&NativeMapView::resizeFramebuffer, "nativeResizeFramebuffer"),
|
||||
METHOD(&NativeMapView::createSurface, "nativeCreateSurface"),
|
||||
METHOD(&NativeMapView::destroySurface, "nativeDestroySurface"),
|
||||
METHOD(&NativeMapView::getStyleUrl, "nativeGetStyleUrl"),
|
||||
METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"),
|
||||
METHOD(&NativeMapView::getStyleJson, "nativeGetStyleJson"),
|
||||
@@ -1498,5 +1091,5 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class AndroidRendererBackend;
|
||||
class AndroidRendererFrontend;
|
||||
|
||||
class NativeMapView : public MapObserver {
|
||||
@@ -78,18 +77,19 @@ public:
|
||||
|
||||
// JNI //
|
||||
|
||||
// Called on OpenGL Thread
|
||||
void onSurfaceCreated(jni::JNIEnv&);
|
||||
|
||||
// Called on OpenGL Thread
|
||||
void render(jni::JNIEnv&);
|
||||
|
||||
void update(jni::JNIEnv&);
|
||||
|
||||
void resizeView(jni::JNIEnv&, int, int);
|
||||
|
||||
// Called on OpenGL Thread
|
||||
void resizeFramebuffer(jni::JNIEnv&, int, int);
|
||||
|
||||
void createSurface(jni::JNIEnv&, jni::Object<>);
|
||||
|
||||
void destroySurface(jni::JNIEnv&);
|
||||
|
||||
jni::String getStyleUrl(jni::JNIEnv&);
|
||||
|
||||
void setStyleUrl(jni::JNIEnv&, jni::String);
|
||||
@@ -257,25 +257,11 @@ public:
|
||||
jni::jboolean getPrefetchesTiles(JNIEnv&);
|
||||
|
||||
private:
|
||||
void _initializeDisplay();
|
||||
|
||||
void _terminateDisplay();
|
||||
|
||||
void _initializeContext();
|
||||
|
||||
void _terminateContext();
|
||||
|
||||
void _createSurface(ANativeWindow*);
|
||||
|
||||
void _destroySurface();
|
||||
|
||||
EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
|
||||
|
||||
void updateFps();
|
||||
|
||||
private:
|
||||
std::unique_ptr<AndroidRendererFrontend> rendererFrontend;
|
||||
std::unique_ptr<AndroidRendererBackend> rendererBackend;
|
||||
|
||||
JavaVM *vm = nullptr;
|
||||
jni::UniqueWeakObject<NativeMapView> javaPeer;
|
||||
@@ -283,28 +269,14 @@ private:
|
||||
std::string styleUrl;
|
||||
std::string apiKey;
|
||||
|
||||
ANativeWindow *window = nullptr;
|
||||
|
||||
EGLConfig config = nullptr;
|
||||
EGLint format = -1;
|
||||
|
||||
EGLDisplay display = EGL_NO_DISPLAY;
|
||||
EGLSurface surface = EGL_NO_SURFACE;
|
||||
EGLContext context = EGL_NO_CONTEXT;
|
||||
|
||||
|
||||
float pixelRatio;
|
||||
bool fpsEnabled = false;
|
||||
bool snapshot = false;
|
||||
bool firstRender = true;
|
||||
double fps = 0.0;
|
||||
|
||||
// Minimum texture size according to OpenGL ES 2.0 specification.
|
||||
int width = 64;
|
||||
int height = 64;
|
||||
|
||||
bool framebufferSizeChanged = true;
|
||||
|
||||
// Ensure these are initialised last
|
||||
std::shared_ptr<mbgl::ThreadPool> threadPool;
|
||||
std::unique_ptr<mbgl::Map> map;
|
||||
|
||||
Reference in New Issue
Block a user