This repository has been archived by the owner on Aug 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[android] ported egl configuration chooser
- ported the code previously in the native_map_view to java so it can be more easily used by the GLSurfaceView
- Loading branch information
1 parent
180d086
commit dd61432
Showing
4 changed files
with
343 additions
and
2 deletions.
There are no files selected for viewing
293 changes: 293 additions & 0 deletions
293
...m/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |