package com.jme3.system.android; import android.opengl.GLSurfaceView.EGLConfigChooser; import com.jme3.renderer.android.RendererUtil; import com.jme3.system.AppSettings; import java.util.logging.Level; import java.util.logging.Logger; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; /** * AndroidConfigChooser is used to determine the best suited EGL Config * * @author iwgeric */ public class AndroidConfigChooser implements EGLConfigChooser { private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName()); protected AppSettings settings; private final static int EGL_OPENGL_ES2_BIT = 4; public AndroidConfigChooser(AppSettings settings) { this.settings = settings; } /** * Gets called by the GLSurfaceView class to return the best config */ @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { logger.fine("GLSurfaceView asking for egl config"); Config requestedConfig = getRequestedConfig(); EGLConfig[] configs = getConfigs(egl, display); // First try to find an exact match, but allowing a higher stencil EGLConfig choosenConfig = chooseConfig(egl, display, configs, requestedConfig, false, false, false, true); if (choosenConfig == null && requestedConfig.d > 16) { logger.log(Level.INFO, "EGL configuration not found, reducing depth"); requestedConfig.d = 16; choosenConfig = chooseConfig(egl, display, configs, requestedConfig, false, false, false, true); } if (choosenConfig == null) { logger.log(Level.INFO, "EGL configuration not found, allowing higher RGB"); choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, false, false, true); } if (choosenConfig == null && requestedConfig.a > 0) { logger.log(Level.INFO, "EGL configuration not found, allowing higher alpha"); choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, true, false, true); } if (choosenConfig == null && requestedConfig.s > 0) { logger.log(Level.INFO, "EGL configuration not found, allowing higher samples"); choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, true, true, true); } if (choosenConfig == null && requestedConfig.a > 0) { logger.log(Level.INFO, "EGL configuration not found, reducing alpha"); requestedConfig.a = 1; choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, true, false, true); } if (choosenConfig == null && requestedConfig.s > 0) { logger.log(Level.INFO, "EGL configuration not found, reducing samples"); requestedConfig.s = 1; if (requestedConfig.a > 0) { choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, true, true, true); } else { choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, false, true, true); } } if (choosenConfig == null && requestedConfig.getBitsPerPixel() > 16) { logger.log(Level.INFO, "EGL configuration not found, setting to RGB565"); requestedConfig.r = 5; requestedConfig.g = 6; requestedConfig.b = 5; choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, false, false, true); if (choosenConfig == null) { logger.log(Level.INFO, "EGL configuration not found, allowing higher alpha"); choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, true, false, true); } } if (choosenConfig == null) { logger.log(Level.INFO, "EGL configuration not found, looking for best config with >= 16 bit Depth"); //failsafe, should pick best config with at least 16 depth requestedConfig = new Config(0, 0, 0, 0, 16, 0, 0); choosenConfig = chooseConfig(egl, display, configs, requestedConfig, true, false, false, true); } if (choosenConfig != null) { logger.fine("GLSurfaceView asks for egl config, returning: "); logEGLConfig(choosenConfig, display, egl, Level.FINE); storeSelectedConfig(egl, display, choosenConfig); return choosenConfig; } else { logger.severe("No EGL Config found"); return null; } } private Config getRequestedConfig() { int r, g, b; if (settings.getBitsPerPixel() == 24) { r = g = b = 8; } else { if (settings.getBitsPerPixel() != 16) { logger.log(Level.SEVERE, "Invalid bitsPerPixel setting: {0}, setting to RGB565 (16)", settings.getBitsPerPixel()); settings.setBitsPerPixel(16); } r = 5; g = 6; b = 5; } logger.log(Level.FINE, "Requested Display Config:"); logger.log(Level.FINE, "RGB: {0}, alpha: {1}, depth: {2}, samples: {3}, stencil: {4}", new Object[]{settings.getBitsPerPixel(), settings.getAlphaBits(), settings.getDepthBits(), settings.getSamples(), settings.getStencilBits()}); return new Config( r, g, b, settings.getAlphaBits(), settings.getDepthBits(), settings.getSamples(), settings.getStencilBits()); } /** * Query egl for the available configs * @param egl * @param display * @return */ private EGLConfig[] getConfigs(EGL10 egl, EGLDisplay display) { int[] num_config = new int[1]; int[] configSpec = new int[]{ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE}; if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) { RendererUtil.checkEGLError(egl); throw new AssertionError(); } int numConfigs = num_config[0]; EGLConfig[] configs = new EGLConfig[numConfigs]; if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, num_config)) { RendererUtil.checkEGLError(egl); throw new AssertionError(); } logger.fine("--------------Display Configurations---------------"); for (EGLConfig eGLConfig : configs) { logEGLConfig(eGLConfig, display, egl, Level.FINE); logger.fine("----------------------------------------"); } return configs; } private EGLConfig chooseConfig( EGL10 egl, EGLDisplay display, EGLConfig[] configs, Config requestedConfig, boolean higherRGB, boolean higherAlpha, boolean higherSamples, boolean higherStencil) { EGLConfig keptConfig = null; int kr = 0; int kg = 0; int kb = 0; int ka = 0; int kd = 0; int ks = 0; int kst = 0; // first pass through config list. Try to find an exact match. for (EGLConfig config : configs) { int r = eglGetConfigAttribSafe(egl, display, config, EGL10.EGL_RED_SIZE); int g = eglGetConfigAttribSafe(egl, display, config, EGL10.EGL_GREEN_SIZE); int b = eglGetConfigAttribSafe(egl, display, config, EGL10.EGL_BLUE_SIZE); int a = eglGetConfigAttribSafe(egl, display, config, EGL10.EGL_ALPHA_SIZE); int d = eglGetConfigAttribSafe(egl, display, config, EGL10.EGL_DEPTH_SIZE); int s = eglGetConfigAttribSafe(egl, display, config, EGL10.EGL_SAMPLES); int st = eglGetConfigAttribSafe(egl, display, config, EGL10.EGL_STENCIL_SIZE); logger.log(Level.FINE, "Checking Config r: {0}, g: {1}, b: {2}, alpha: {3}, depth: {4}, samples: {5}, stencil: {6}", new Object[]{r, g, b, a, d, s, st}); if (higherRGB && r < requestedConfig.r) { continue; } if (!higherRGB && r != requestedConfig.r) { continue; } if (higherRGB && g < requestedConfig.g) { continue; } if (!higherRGB && g != requestedConfig.g) { continue; } if (higherRGB && b < requestedConfig.b) { continue; } if (!higherRGB && b != requestedConfig.b) { continue; } if (higherAlpha && a < requestedConfig.a) { continue; } if (!higherAlpha && a != requestedConfig.a) { continue; } if (d < requestedConfig.d) { continue; } // always allow higher depth if (higherSamples && s < requestedConfig.s) { continue; } if (!higherSamples && s != requestedConfig.s) { continue; } if (higherStencil && st < requestedConfig.st) { continue; } if (!higherStencil && !inRange(st, 0, requestedConfig.st)) { continue; } //we keep the config if it is better if ( r >= kr || g >= kg || b >= kb || a >= ka || d >= kd || s >= ks || st >= kst ) { kr = r; kg = g; kb = b; ka = a; kd = d; ks = s; kst = st; keptConfig = config; logger.log(Level.FINE, "Keeping Config r: {0}, g: {1}, b: {2}, alpha: {3}, depth: {4}, samples: {5}, stencil: {6}", new Object[]{r, g, b, a, d, s, st}); } } if (keptConfig != null) { return keptConfig; } //no match found logger.log(Level.SEVERE, "No egl config match found"); return null; } private static int eglGetConfigAttribSafe(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute) { int[] value = new int[1]; if (!egl.eglGetConfigAttrib(display, config, attribute, value)) { RendererUtil.checkEGLError(egl); throw new AssertionError(); } return value[0]; } private void storeSelectedConfig(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { int r = eglGetConfigAttribSafe(egl, display, eglConfig, EGL10.EGL_RED_SIZE); int g = eglGetConfigAttribSafe(egl, display, eglConfig, EGL10.EGL_GREEN_SIZE); int b = eglGetConfigAttribSafe(egl, display, eglConfig, EGL10.EGL_BLUE_SIZE); settings.setBitsPerPixel(r+g+b); settings.setAlphaBits( eglGetConfigAttribSafe(egl, display, eglConfig, EGL10.EGL_ALPHA_SIZE)); settings.setDepthBits( eglGetConfigAttribSafe(egl, display, eglConfig, EGL10.EGL_DEPTH_SIZE)); settings.setSamples( eglGetConfigAttribSafe(egl, display, eglConfig, EGL10.EGL_SAMPLES)); settings.setStencilBits( eglGetConfigAttribSafe(egl, display, eglConfig, EGL10.EGL_STENCIL_SIZE)); } /** * log output with egl config details * * @param conf * @param display * @param egl */ private void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl, Level level) { logger.log(level, "EGL_RED_SIZE = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_RED_SIZE)); logger.log(level, "EGL_GREEN_SIZE = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_GREEN_SIZE)); logger.log(level, "EGL_BLUE_SIZE = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_BLUE_SIZE)); logger.log(level, "EGL_ALPHA_SIZE = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_ALPHA_SIZE)); logger.log(level, "EGL_DEPTH_SIZE = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_DEPTH_SIZE)); logger.log(level, "EGL_STENCIL_SIZE = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_STENCIL_SIZE)); logger.log(level, "EGL_RENDERABLE_TYPE = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_RENDERABLE_TYPE)); logger.log(level, "EGL_SURFACE_TYPE = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_SURFACE_TYPE)); logger.log(level, "EGL_SAMPLE_BUFFERS = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_SAMPLE_BUFFERS)); logger.log(level, "EGL_SAMPLES = {0}", eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_SAMPLES)); } private boolean inRange(int val, int min, int max) { return min <= val && val <= max; } private class Config { /** * red, green, blue, alpha, depth, samples, stencil */ int r, g, b, a, d, s, st; private Config(int r, int g, int b, int a, int d, int s, int st) { this.r = r; this.g = g; this.b = b; this.a = a; this.d = d; this.s = s; this.st = st; } private int getBitsPerPixel() { return r+g+b; } } //DON'T REMOVE THIS, USED FOR UNIT TESTING FAILING CONFIGURATION LISTS. // private static class Config { // // int r, g, b, a, d, s, ms, ns; // // public Config(int r, int g, int b, int a, int d, int s, int ms, int ns) { // this.r = r; // this.g = g; // this.b = b; // this.a = a; // this.d = d; // this.s = s; // this.ms = ms; // this.ns = ns; // } // // @Override // public String toString() { // return "Config{" + "r=" + r + ", g=" + g + ", b=" + b + ", a=" + a + ", d=" + d + ", s=" + s + ", ms=" + ms + ", ns=" + ns + '}'; // } // } // // public static Config chooseConfig(List<Config> configs, ConfigType configType, int mSamples) { // // Config keptConfig = null; // int kd = 0; // int knbMs = 0; // // // // first pass through config list. Try to find an exact match. // for (Config config : configs) { //// logEGLConfig(config, display, egl); // int r = config.r; // int g = config.g; // int b = config.b; // int a = config.a; // int d = config.d; // int s = config.s; // int isMs = config.ms; // int nbMs = config.ns; // // if (inRange(r, configType.mr, configType.r) // && inRange(g, configType.mg, configType.g) // && inRange(b, configType.mb, configType.b) // && inRange(a, configType.ma, configType.a) // && inRange(d, configType.md, configType.d) // && inRange(s, configType.ms, configType.s)) { // if (mSamples == 0 && isMs != 0) { // continue; // } // boolean keep = false; // //we keep the config if the depth is better or if the AA setting is better // if (d >= kd) { // kd = d; // keep = true; // } else { // keep = false; // } // // if (mSamples != 0) { // if (nbMs >= knbMs && nbMs <= mSamples) { // knbMs = nbMs; // keep = true; // } else { // keep = false; // } // } // // if (keep) { // keptConfig = config; // } // } // } // // if (keptConfig != null) { // return keptConfig; // } // // if (configType == ConfigType.BEST) { // keptConfig = chooseConfig(configs, ConfigType.BEST_TRANSLUCENT, mSamples); // // if (keptConfig != null) { // return keptConfig; // } // } // // if (configType == ConfigType.BEST_TRANSLUCENT) { // keptConfig = chooseConfig(configs, ConfigType.FASTEST, mSamples); // // if (keptConfig != null) { // return keptConfig; // } // } // // failsafe. pick the 1st config. // // for (Config config : configs) { // if (config.d >= 16) { // return config; // } // } // // return null; // } // // private static boolean inRange(int val, int min, int max) { // return min <= val && val <= max; // } // // public static void main(String... argv) { // List<Config> confs = new ArrayList<Config>(); // confs.add(new Config(5, 6, 5, 0, 0, 0, 0, 0)); // confs.add(new Config(5, 6, 5, 0, 16, 0, 0, 0)); // confs.add(new Config(5, 6, 5, 0, 24, 8, 0, 0)); // confs.add(new Config(8, 8, 8, 8, 0, 0, 0, 0)); //// confs.add(new Config(8, 8, 8, 8, 16, 0, 0, 0)); //// confs.add(new Config(8, 8, 8, 8, 24, 8, 0, 0)); // // confs.add(new Config(5, 6, 5, 0, 0, 0, 1, 2)); // confs.add(new Config(5, 6, 5, 0, 16, 0, 1, 2)); // confs.add(new Config(5, 6, 5, 0, 24, 8, 1, 2)); // confs.add(new Config(8, 8, 8, 8, 0, 0, 1, 2)); //// confs.add(new Config(8, 8, 8, 8, 16, 0, 1, 2)); //// confs.add(new Config(8, 8, 8, 8, 24, 8, 1, 2)); // // confs.add(new Config(5, 6, 5, 0, 0, 0, 1, 4)); // confs.add(new Config(5, 6, 5, 0, 16, 0, 1, 4)); // confs.add(new Config(5, 6, 5, 0, 24, 8, 1, 4)); // confs.add(new Config(8, 8, 8, 8, 0, 0, 1, 4)); //// confs.add(new Config(8, 8, 8, 8, 16, 0, 1, 4)); //// confs.add(new Config(8, 8, 8, 8, 24, 8, 1, 4)); // // Config chosen = chooseConfig(confs, ConfigType.BEST, 0); // // System.err.println(chosen); // // } }