1package com.jme3.system.android;
2
3import android.graphics.PixelFormat;
4import android.opengl.GLSurfaceView.EGLConfigChooser;
5import java.util.logging.Level;
6import java.util.logging.Logger;
7import javax.microedition.khronos.egl.EGL10;
8import javax.microedition.khronos.egl.EGLConfig;
9import javax.microedition.khronos.egl.EGLDisplay;
10
11/**
12 * AndroidConfigChooser is used to determine the best suited EGL Config
13 *
14 * @author larynx
15 */
16public class AndroidConfigChooser implements EGLConfigChooser {
17
18    private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName());
19    protected int clientOpenGLESVersion = 0;
20    protected EGLConfig bestConfig = null;
21    protected EGLConfig fastestConfig = null;
22    protected EGLConfig choosenConfig = null;
23    protected ConfigType type;
24    protected int pixelFormat;
25    protected boolean verbose = false;
26    private final static int EGL_OPENGL_ES2_BIT = 4;
27
28    public enum ConfigType {
29
30        /**
31         * RGB565, 0 alpha, 16 depth, 0 stencil
32         */
33        FASTEST,
34        /**
35         * RGB???, 0 alpha, >=16 depth, 0 stencil
36         */
37        BEST,
38        /**
39         * Turn off config chooser and use hardcoded
40         * setEGLContextClientVersion(2); setEGLConfigChooser(5, 6, 5, 0, 16,
41         * 0);
42         */
43        LEGACY
44    }
45
46    public AndroidConfigChooser(ConfigType type) {
47        this.type = type;
48    }
49
50    /**
51     * Gets called by the GLSurfaceView class to return the best config
52     */
53    @Override
54    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
55        logger.info("GLSurfaceView asks for egl config, returning: ");
56        logEGLConfig(choosenConfig, display, egl);
57        return choosenConfig;
58    }
59
60    /**
61     * findConfig is used to locate the best config and init the chooser with
62     *
63     * @param egl
64     * @param display
65     * @return true if successfull, false if no config was found
66     */
67    public boolean findConfig(EGL10 egl, EGLDisplay display) {
68
69        if (type == ConfigType.BEST) {
70            ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0);
71            choosenConfig = compChooser.chooseConfig(egl, display);
72
73            if (choosenConfig == null) {
74                compChooser = new ComponentSizeChooser(8, 8, 8, 0, 32, 0);
75                choosenConfig = compChooser.chooseConfig(egl, display);
76                if (choosenConfig == null) {
77                    compChooser = new ComponentSizeChooser(8, 8, 8, 8, 16, 0);
78                    choosenConfig = compChooser.chooseConfig(egl, display);
79                    if (choosenConfig == null) {
80                        compChooser = new ComponentSizeChooser(8, 8, 8, 0, 16, 0);
81                        choosenConfig = compChooser.chooseConfig(egl, display);
82                    }
83                }
84            }
85
86            logger.info("JME3 using best EGL configuration available here: ");
87        } else {
88            ComponentSizeChooser compChooser = new ComponentSizeChooser(5, 6, 5, 0, 16, 0);
89            choosenConfig = compChooser.chooseConfig(egl, display);
90            logger.info("JME3 using fastest EGL configuration available here: ");
91        }
92
93        if (choosenConfig != null) {
94            logger.info("JME3 using choosen config: ");
95            logEGLConfig(choosenConfig, display, egl);
96            pixelFormat = getPixelFormat(choosenConfig, display, egl);
97            clientOpenGLESVersion = getOpenGLVersion(choosenConfig, display, egl);
98            return true;
99        } else {
100            logger.severe("###ERROR### Unable to get a valid OpenGL ES 2.0 config, nether Fastest nor Best found! Bug. Please report this.");
101            clientOpenGLESVersion = 1;
102            pixelFormat = PixelFormat.UNKNOWN;
103            return false;
104        }
105    }
106
107    private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl) {
108        int[] value = new int[1];
109        int result = PixelFormat.RGB_565;
110
111        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value);
112        if (value[0] == 8) {
113            result = PixelFormat.RGBA_8888;
114            /*
115            egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value);
116            if (value[0] == 8)
117            {
118                result = PixelFormat.RGBA_8888;
119            }
120            else
121            {
122                result = PixelFormat.RGB_888;
123            }*/
124        }
125
126        if (verbose) {
127            logger.log(Level.INFO, "Using PixelFormat {0}", result);
128        }
129
130        //return result; TODO Test pixelformat
131        return PixelFormat.TRANSPARENT;
132    }
133
134    private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl) {
135        int[] value = new int[1];
136        int result = 1;
137
138        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value);
139        // Check if conf is OpenGL ES 2.0
140        if ((value[0] & EGL_OPENGL_ES2_BIT) != 0) {
141            result = 2;
142        }
143
144        return result;
145    }
146
147    /**
148     * log output with egl config details
149     *
150     * @param conf
151     * @param display
152     * @param egl
153     */
154    public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl) {
155        int[] value = new int[1];
156
157        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value);
158        logger.info(String.format("EGL_RED_SIZE  = %d", value[0]));
159
160        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_GREEN_SIZE, value);
161        logger.info(String.format("EGL_GREEN_SIZE  = %d", value[0]));
162
163        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_BLUE_SIZE, value);
164        logger.info(String.format("EGL_BLUE_SIZE  = %d", value[0]));
165
166        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value);
167        logger.info(String.format("EGL_ALPHA_SIZE  = %d", value[0]));
168
169        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_DEPTH_SIZE, value);
170        logger.info(String.format("EGL_DEPTH_SIZE  = %d", value[0]));
171
172        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_STENCIL_SIZE, value);
173        logger.info(String.format("EGL_STENCIL_SIZE  = %d", value[0]));
174
175        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value);
176        logger.info(String.format("EGL_RENDERABLE_TYPE  = %d", value[0]));
177
178        egl.eglGetConfigAttrib(display, conf, EGL10.EGL_SURFACE_TYPE, value);
179        logger.info(String.format("EGL_SURFACE_TYPE  = %d", value[0]));
180    }
181
182    public int getClientOpenGLESVersion() {
183        return clientOpenGLESVersion;
184    }
185
186    public void setClientOpenGLESVersion(int clientOpenGLESVersion) {
187        this.clientOpenGLESVersion = clientOpenGLESVersion;
188    }
189
190    public int getPixelFormat() {
191        return pixelFormat;
192    }
193
194    private abstract class BaseConfigChooser implements EGLConfigChooser {
195
196        private boolean bClientOpenGLESVersionSet;
197
198        public BaseConfigChooser(int[] configSpec) {
199            bClientOpenGLESVersionSet = false;
200            mConfigSpec = filterConfigSpec(configSpec);
201        }
202
203        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
204            int[] num_config = new int[1];
205            if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
206                    num_config)) {
207                throw new IllegalArgumentException("eglChooseConfig failed");
208            }
209
210            int numConfigs = num_config[0];
211
212            if (numConfigs <= 0) {
213                //throw new IllegalArgumentException("No configs match configSpec");
214
215                return null;
216            }
217
218            EGLConfig[] configs = new EGLConfig[numConfigs];
219            if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
220                    num_config)) {
221                throw new IllegalArgumentException("eglChooseConfig#2 failed");
222            }
223            EGLConfig config = chooseConfig(egl, display, configs);
224            //if (config == null) {
225            //    throw new IllegalArgumentException("No config chosen");
226            //}
227            return config;
228        }
229
230        abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
231                EGLConfig[] configs);
232        protected int[] mConfigSpec;
233
234        private int[] filterConfigSpec(int[] configSpec) {
235            if (bClientOpenGLESVersionSet == true) {
236                return configSpec;
237            }
238            /*
239             * We know none of the subclasses define EGL_RENDERABLE_TYPE. And we
240             * know the configSpec is well formed.
241             */
242            int len = configSpec.length;
243            int[] newConfigSpec = new int[len + 2];
244            System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
245            newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
246            newConfigSpec[len] = 4; /*
247             * EGL_OPENGL_ES2_BIT
248             */
249            newConfigSpec[len + 1] = EGL10.EGL_NONE;
250
251            bClientOpenGLESVersionSet = true;
252
253            return newConfigSpec;
254        }
255    }
256
257    /**
258     * Choose a configuration with exactly the specified r,g,b,a sizes, and at
259     * least the specified depth and stencil sizes.
260     */
261    private class ComponentSizeChooser extends BaseConfigChooser {
262
263        private int[] mValue;
264        // Subclasses can adjust these values:
265        protected int mRedSize;
266        protected int mGreenSize;
267        protected int mBlueSize;
268        protected int mAlphaSize;
269        protected int mDepthSize;
270        protected int mStencilSize;
271
272        public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
273                int alphaSize, int depthSize, int stencilSize) {
274            super(new int[]{
275                        EGL10.EGL_RED_SIZE, redSize,
276                        EGL10.EGL_GREEN_SIZE, greenSize,
277                        EGL10.EGL_BLUE_SIZE, blueSize,
278                        EGL10.EGL_ALPHA_SIZE, alphaSize,
279                        EGL10.EGL_DEPTH_SIZE, depthSize,
280                        EGL10.EGL_STENCIL_SIZE, stencilSize,
281                        EGL10.EGL_NONE});
282            mValue = new int[1];
283            mRedSize = redSize;
284            mGreenSize = greenSize;
285            mBlueSize = blueSize;
286            mAlphaSize = alphaSize;
287            mDepthSize = depthSize;
288            mStencilSize = stencilSize;
289        }
290
291        @Override
292        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
293            for (EGLConfig config : configs) {
294                int d = findConfigAttrib(egl, display, config,
295                        EGL10.EGL_DEPTH_SIZE, 0);
296                int s = findConfigAttrib(egl, display, config,
297                        EGL10.EGL_STENCIL_SIZE, 0);
298                if ((d >= mDepthSize) && (s >= mStencilSize)) {
299                    int r = findConfigAttrib(egl, display, config,
300                            EGL10.EGL_RED_SIZE, 0);
301                    int g = findConfigAttrib(egl, display, config,
302                            EGL10.EGL_GREEN_SIZE, 0);
303                    int b = findConfigAttrib(egl, display, config,
304                            EGL10.EGL_BLUE_SIZE, 0);
305                    int a = findConfigAttrib(egl, display, config,
306                            EGL10.EGL_ALPHA_SIZE, 0);
307                    if ((r == mRedSize) && (g == mGreenSize)
308                            && (b == mBlueSize) && (a == mAlphaSize)) {
309                        return config;
310                    }
311                }
312            }
313            return null;
314        }
315
316        private int findConfigAttrib(EGL10 egl, EGLDisplay display,
317                EGLConfig config, int attribute, int defaultValue) {
318
319            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
320                return mValue[0];
321            }
322            return defaultValue;
323        }
324    }
325}
326