package org.andengine.opengl.view;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import android.opengl.GLSurfaceView;
/**
* (c) Zynga 2011
*
* @author Nicolas Gramlich <ngramlich@zynga.com>
* @since 15:31:48 - 04.08.2011
*/
public class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
// ===========================================================
// Constants
// ===========================================================
private static final int[] BUFFER = new int[1];
private static final int RED_SIZE = 5;
private static final int GREEN_SIZE = 6;
private static final int BLUE_SIZE = 5;
private static final int DEPTH_SIZE = 0;
private static final int ALPHA_SIZE = 0;
private static final int STENCIL_SIZE = 0;
private static final int MULTISAMPLE_COUNT = 2; // TODO Could be made variable?
private static final int EGL_GLES2_BIT = 4;
private static final int[] EGLCONFIG_ATTRIBUTES_MULTISAMPLE = {
EGL10.EGL_RED_SIZE, ConfigChooser.RED_SIZE,
EGL10.EGL_GREEN_SIZE, ConfigChooser.GREEN_SIZE,
EGL10.EGL_BLUE_SIZE, ConfigChooser.BLUE_SIZE,
EGL10.EGL_ALPHA_SIZE, ConfigChooser.ALPHA_SIZE,
EGL10.EGL_DEPTH_SIZE, ConfigChooser.DEPTH_SIZE,
EGL10.EGL_STENCIL_SIZE, ConfigChooser.STENCIL_SIZE,
EGL10.EGL_RENDERABLE_TYPE, ConfigChooser.EGL_GLES2_BIT,
EGL10.EGL_SAMPLE_BUFFERS, 1,
EGL10.EGL_SAMPLES, ConfigChooser.MULTISAMPLE_COUNT,
EGL10.EGL_NONE
};
private static final int EGL_COVERAGE_BUFFERS_NV = 0x30E0;
private static final int EGL_COVERAGE_SAMPLES_NV = 0x30E1;
private static final int[] EGLCONFIG_ATTRIBUTES_COVERAGEMULTISAMPLE_NVIDIA = new int[]{
EGL10.EGL_RED_SIZE, ConfigChooser.RED_SIZE,
EGL10.EGL_GREEN_SIZE, ConfigChooser.GREEN_SIZE,
EGL10.EGL_BLUE_SIZE, ConfigChooser.BLUE_SIZE,
EGL10.EGL_ALPHA_SIZE, ConfigChooser.ALPHA_SIZE,
EGL10.EGL_DEPTH_SIZE, ConfigChooser.DEPTH_SIZE,
EGL10.EGL_STENCIL_SIZE, ConfigChooser.STENCIL_SIZE,
EGL10.EGL_RENDERABLE_TYPE, ConfigChooser.EGL_GLES2_BIT,
ConfigChooser.EGL_COVERAGE_BUFFERS_NV, 1,
ConfigChooser.EGL_COVERAGE_SAMPLES_NV, ConfigChooser.MULTISAMPLE_COUNT, // always 5 in practice on tegra 2
EGL10.EGL_NONE
};
private static final int[] EGLCONFIG_ATTRIBUTES_FALLBACK = new int[] {
EGL10.EGL_RED_SIZE, ConfigChooser.RED_SIZE,
EGL10.EGL_GREEN_SIZE, ConfigChooser.GREEN_SIZE,
EGL10.EGL_BLUE_SIZE, ConfigChooser.BLUE_SIZE,
EGL10.EGL_ALPHA_SIZE, ConfigChooser.ALPHA_SIZE,
EGL10.EGL_DEPTH_SIZE, ConfigChooser.DEPTH_SIZE,
EGL10.EGL_STENCIL_SIZE, ConfigChooser.STENCIL_SIZE,
EGL10.EGL_RENDERABLE_TYPE, ConfigChooser.EGL_GLES2_BIT,
EGL10.EGL_NONE
};
// ===========================================================
// Fields
// ===========================================================
private final boolean mMultiSamplingRequested;
private boolean mMultiSampling;
private boolean mCoverageMultiSampling;
private int mRedSize = -1;
private int mGreenSize = -1;
private int mBlueSize = -1;
private int mAlphaSize = -1;
private int mDepthSize = -1;
private int mStencilSize = -1;
// ===========================================================
// Constructors
// ===========================================================
public ConfigChooser(final boolean pMultiSamplingRequested) {
this.mMultiSamplingRequested = pMultiSamplingRequested;
}
// ===========================================================
// Getter & Setter
// ===========================================================
public boolean isMultiSampling() {
return this.mMultiSampling;
}
public boolean isCoverageMultiSampling() {
return this.mCoverageMultiSampling;
}
public int getRedSize() {
return this.mRedSize;
}
public int getGreenSize() {
return this.mGreenSize;
}
public int getBlueSize() {
return this.mBlueSize;
}
public int getAlphaSize() {
return this.mAlphaSize;
}
public int getDepthSize() {
return this.mDepthSize;
}
public int getStencilSize() {
return this.mStencilSize;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public EGLConfig chooseConfig(final EGL10 pEGL, final EGLDisplay pEGLDisplay) {
try {
return this.chooseConfig(pEGL, pEGLDisplay, ConfigChooserMatcher.STRICT);
} catch (final IllegalArgumentException e) {
}
try {
return this.chooseConfig(pEGL, pEGLDisplay, ConfigChooserMatcher.LOOSE_STENCIL);
} catch (final IllegalArgumentException e) {
}
try {
return this.chooseConfig(pEGL, pEGLDisplay, ConfigChooserMatcher.LOOSE_DEPTH_AND_STENCIL);
} catch (final IllegalArgumentException e) {
}
return this.chooseConfig(pEGL, pEGLDisplay, ConfigChooserMatcher.ANY);
}
private EGLConfig chooseConfig(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final ConfigChooserMatcher pConfigChooserMatcher) throws IllegalArgumentException {
ConfigChooser.BUFFER[0] = 0;
int eglConfigCount;
if(this.mMultiSamplingRequested) {
eglConfigCount = ConfigChooser.getEGLConfigCount(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_MULTISAMPLE);
if(eglConfigCount > 0) {
this.mMultiSampling = true;
return this.findEGLConfig(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_MULTISAMPLE, eglConfigCount, pConfigChooserMatcher);
}
eglConfigCount = ConfigChooser.getEGLConfigCount(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_COVERAGEMULTISAMPLE_NVIDIA);
if(eglConfigCount > 0) {
this.mCoverageMultiSampling = true;
return this.findEGLConfig(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_COVERAGEMULTISAMPLE_NVIDIA, eglConfigCount, pConfigChooserMatcher);
}
}
eglConfigCount = ConfigChooser.getEGLConfigCount(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_FALLBACK);
if(eglConfigCount > 0) {
return this.findEGLConfig(pEGL, pEGLDisplay, ConfigChooser.EGLCONFIG_ATTRIBUTES_FALLBACK, eglConfigCount, pConfigChooserMatcher);
} else {
throw new IllegalArgumentException("No " + EGLConfig.class.getSimpleName() + " found!");
}
}
// ===========================================================
// Methods
// ===========================================================
private static int getEGLConfigCount(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final int[] pEGLConfigAttributes) {
if(pEGL.eglChooseConfig(pEGLDisplay, pEGLConfigAttributes, null, 0, ConfigChooser.BUFFER) == false) {
throw new IllegalArgumentException("EGLCONFIG_FALLBACK failed!");
}
return ConfigChooser.BUFFER[0];
}
private EGLConfig findEGLConfig(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final int[] pEGLConfigAttributes, final int pEGLConfigCount, final ConfigChooserMatcher pConfigChooserMatcher) {
final EGLConfig[] eglConfigs = new EGLConfig[pEGLConfigCount];
if(!pEGL.eglChooseConfig(pEGLDisplay, pEGLConfigAttributes, eglConfigs, pEGLConfigCount, ConfigChooser.BUFFER)) {
throw new IllegalArgumentException("findEGLConfig failed!");
}
return this.findEGLConfig(pEGL, pEGLDisplay, eglConfigs, pConfigChooserMatcher);
}
private EGLConfig findEGLConfig(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final EGLConfig[] pEGLConfigs, final ConfigChooserMatcher pConfigChooserMatcher) {
for(int i = 0; i < pEGLConfigs.length; ++i) {
final EGLConfig config = pEGLConfigs[i];
if(config != null) {
final int redSize = ConfigChooser.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_RED_SIZE, 0);
final int greenSize = ConfigChooser.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_GREEN_SIZE, 0);
final int blueSize = ConfigChooser.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_BLUE_SIZE, 0);
final int alphaSize = ConfigChooser.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_ALPHA_SIZE, 0);
final int depthSize = ConfigChooser.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_DEPTH_SIZE, 0);
final int stencilSize = ConfigChooser.getConfigAttrib(pEGL, pEGLDisplay, config, EGL10.EGL_STENCIL_SIZE, 0);
if(pConfigChooserMatcher.matches(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize)) {
this.mRedSize = redSize;
this.mGreenSize = greenSize;
this.mBlueSize = blueSize;
this.mAlphaSize = alphaSize;
this.mDepthSize = depthSize;
this.mStencilSize = stencilSize;
return config;
}
}
}
throw new IllegalArgumentException("No EGLConfig found!");
}
private static int getConfigAttrib(final EGL10 pEGL, final EGLDisplay pEGLDisplay, final EGLConfig pEGLConfig, final int pAttribute, final int pDefaultValue) {
if(pEGL.eglGetConfigAttrib(pEGLDisplay, pEGLConfig, pAttribute, ConfigChooser.BUFFER)) {
return ConfigChooser.BUFFER[0];
} else {
return pDefaultValue;
}
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
public enum ConfigChooserMatcher {
// ===========================================================
// Elements
// ===========================================================
STRICT() {
@Override
public boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize) {
if(pDepthSize == ConfigChooser.DEPTH_SIZE && pStencilSize == ConfigChooser.STENCIL_SIZE) {
if(pRedSize == ConfigChooser.RED_SIZE && pGreenSize == ConfigChooser.GREEN_SIZE && pBlueSize == ConfigChooser.BLUE_SIZE && pAlphaSize == ConfigChooser.ALPHA_SIZE) {
return true;
}
}
return false;
}
},
LOOSE_STENCIL() {
@Override
public boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize) {
if(pDepthSize == ConfigChooser.DEPTH_SIZE && pStencilSize >= ConfigChooser.STENCIL_SIZE) {
if(pRedSize == ConfigChooser.RED_SIZE && pGreenSize == ConfigChooser.GREEN_SIZE && pBlueSize == ConfigChooser.BLUE_SIZE && pAlphaSize == ConfigChooser.ALPHA_SIZE) {
return true;
}
}
return false;
}
},
LOOSE_DEPTH_AND_STENCIL() {
@Override
public boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize) {
if(pDepthSize >= ConfigChooser.DEPTH_SIZE && pStencilSize >= ConfigChooser.STENCIL_SIZE) {
if(pRedSize == ConfigChooser.RED_SIZE && pGreenSize == ConfigChooser.GREEN_SIZE && pBlueSize == ConfigChooser.BLUE_SIZE && pAlphaSize == ConfigChooser.ALPHA_SIZE) {
return true;
}
}
return false;
}
},
ANY() {
@Override
public boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize) {
return true;
}
};
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
// ===========================================================
// Constructors
// ===========================================================
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
public abstract boolean matches(final int pRedSize, final int pGreenSize, final int pBlueSize, final int pAlphaSize, final int pDepthSize, final int pStencilSize);
// ===========================================================
// Methods
// ===========================================================
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}
}