Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[android] ported egl configuration chooser
Browse files Browse the repository at this point in the history
- ported the code previously in the native_map_view to java so it can be more easily used by the GLSurfaceView
  • Loading branch information
ivovandongen committed Aug 9, 2017
1 parent 180d086 commit dd61432
Show file tree
Hide file tree
Showing 4 changed files with 343 additions and 2 deletions.
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;
}
}
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +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.widgets.CompassView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
Expand Down Expand Up @@ -233,9 +234,8 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
private void initialiseDrawingSurface() {
glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView);
glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
// TODO port config chooser from native code
glSurfaceView.setEGLConfigChooser(8, 8, 8, 0 /** TODO: What alpha value do we need here?? */, 16, 8);
glSurfaceView.setEGLContextClientVersion(2);
glSurfaceView.setEGLConfigChooser(new EGLConfigChooser());
glSurfaceView.setRenderer(new GLSurfaceView.Renderer() {

@Override
Expand Down
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);
}
}

0 comments on commit dd61432

Please sign in to comment.