diff --git a/engine/src/android/com/jme3/renderer/android/RendererUtil.java b/engine/src/android/com/jme3/renderer/android/RendererUtil.java index a84ca1775..ce611044d 100644 --- a/engine/src/android/com/jme3/renderer/android/RendererUtil.java +++ b/engine/src/android/com/jme3/renderer/android/RendererUtil.java @@ -3,23 +3,27 @@ package com.jme3.renderer.android; import android.opengl.GLES20; import android.opengl.GLU; import com.jme3.renderer.RendererException; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; /** - * Utility class used by the {@link OGLESShaderRenderer renderer} and sister classes. - * + * Utility class used by the {@link OGLESShaderRenderer renderer} and sister + * classes. + * * @author Kirill Vainer */ public class RendererUtil { - + /** - * When set to true, every OpenGL call will check for errors and throw - * an exception if there is one, if false, no error checking is performed. + * When set to true, every OpenGL call will check for errors and throw an + * exception if there is one, if false, no error checking is performed. */ public static boolean ENABLE_ERROR_CHECKING = true; - + /** - * Checks for an OpenGL error and throws a {@link RendererException} - * if there is one. Ignores the value of {@link RendererUtil#ENABLE_ERROR_CHECKING}. + * Checks for an OpenGL error and throws a {@link RendererException} if + * there is one. Ignores the value of + * {@link RendererUtil#ENABLE_ERROR_CHECKING}. */ public static void checkGLErrorForced() { int error = GLES20.glGetError(); @@ -32,14 +36,86 @@ public class RendererUtil { } } } - + + /** + * Checks for an EGL error and throws a {@link RendererException} if there + * is one. Ignores the value of {@link RendererUtil#ENABLE_ERROR_CHECKING}. + */ + public static void checkEGLError(EGL10 egl) { + int error = egl.eglGetError(); + if (error != EGL10.EGL_SUCCESS) { + String errorMessage; + switch (error) { + case EGL10.EGL_SUCCESS: + return; + case EGL10.EGL_NOT_INITIALIZED: + errorMessage = "EGL is not initialized, or could not be " + + "initialized, for the specified EGL display connection. "; + break; + case EGL10.EGL_BAD_ACCESS: + errorMessage = "EGL cannot access a requested resource " + + "(for example a context is bound in another thread). "; + break; + case EGL10.EGL_BAD_ALLOC: + errorMessage = "EGL failed to allocate resources for the requested operation."; + break; + case EGL10.EGL_BAD_ATTRIBUTE: + errorMessage = "An unrecognized attribute or attribute " + + "value was passed in the attribute list. "; + break; + case EGL10.EGL_BAD_CONTEXT: + errorMessage = "An EGLContext argument does not name a valid EGL rendering context. "; + break; + case EGL10.EGL_BAD_CONFIG: + errorMessage = "An EGLConfig argument does not name a valid EGL frame buffer configuration. "; + break; + case EGL10.EGL_BAD_CURRENT_SURFACE: + errorMessage = "The current surface of the calling thread " + + "is a window, pixel buffer or pixmap that is no longer valid. "; + break; + case EGL10.EGL_BAD_DISPLAY: + errorMessage = "An EGLDisplay argument does not name a valid EGL display connection. "; + break; + case EGL10.EGL_BAD_SURFACE: + errorMessage = "An EGLSurface argument does not name a " + + "valid surface (window, pixel buffer or pixmap) configured for GL rendering. "; + break; + case EGL10.EGL_BAD_MATCH: + errorMessage = "Arguments are inconsistent (for example, a " + + "valid context requires buffers not supplied by a valid surface). "; + break; + case EGL10.EGL_BAD_PARAMETER: + errorMessage = "One or more argument values are invalid."; + break; + case EGL10.EGL_BAD_NATIVE_PIXMAP: + errorMessage = "A NativePixmapType argument does not refer to a valid native pixmap. "; + break; + case EGL10.EGL_BAD_NATIVE_WINDOW: + errorMessage = "A NativeWindowType argument does not refer to a valid native window. "; + break; + case EGL11.EGL_CONTEXT_LOST: + errorMessage = "A power management event has occurred. " + + "The application must destroy all contexts and reinitialise " + + "OpenGL ES state and objects to continue rendering. "; + break; + default: + errorMessage = "Unknown"; + } + + throw new RendererException("EGL error 0x" + Integer.toHexString(error) + ": " + errorMessage); + } + } + /** - * Checks for an OpenGL error and throws a {@link RendererException} - * if there is one. Does nothing if {@link RendererUtil#ENABLE_ERROR_CHECKING} - * is set to false. + * Checks for an OpenGL error and throws a {@link RendererException} if + * there is one. Does nothing if {@link RendererUtil#ENABLE_ERROR_CHECKING} + * is set to + * false. */ public static void checkGLError() { - if (!ENABLE_ERROR_CHECKING) return; + if (!ENABLE_ERROR_CHECKING) { + return; + } int error = GLES20.glGetError(); if (error != 0) { String message = GLU.gluErrorString(error); diff --git a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java index 2d8acd4d2..fd44a7bb8 100644 --- a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java +++ b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java @@ -2,6 +2,7 @@ package com.jme3.system.android; import android.graphics.PixelFormat; import android.opengl.GLSurfaceView.EGLConfigChooser; +import com.jme3.renderer.android.RendererUtil; import com.jme3.system.AppSettings; import java.util.ArrayList; import java.util.List; @@ -91,6 +92,15 @@ public class AndroidConfigChooser implements EGLConfigChooser { this.settings = settings; } + 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]; + } + /** * Gets called by the GLSurfaceView class to return the best config */ @@ -130,17 +140,16 @@ public class AndroidConfigChooser implements EGLConfigChooser { } private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl) { - int[] value = new int[1]; - //Android Pixel format is not very well documented. //From what i gathered, the format is chosen automatically except for the alpha channel //if the alpha channel has 8 bit or more, e set the pixel format to Transluscent, as it allow transparent view background //if it's 0 bit, the format is OPAQUE otherwise it's TRANSPARENT - egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value); - if (value[0] >= 8) { + int result = eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_ALPHA_SIZE); + + if (result >= 8) { return PixelFormat.TRANSLUCENT; } - if (value[0] >= 1) { + if (result >= 1) { return PixelFormat.TRANSPARENT; } @@ -148,29 +157,15 @@ public class AndroidConfigChooser implements EGLConfigChooser { } private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl) { - int[] value = new int[1]; - int result = 1; - - egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value); + int val = eglGetConfigAttribSafe(egl, display, conf, EGL10.EGL_RENDERABLE_TYPE); // Check if conf is OpenGL ES 2.0 - if ((value[0] & EGL_OPENGL_ES2_BIT) != 0) { - result = 2; - } - - return result; - } - - private void logEGLConfigAttrib (EGLConfig conf, EGLDisplay display, EGL10 egl, - Level level, String configName, int eglConfigAttrib) { - - int[] value = new int[1]; - if (egl.eglGetConfigAttrib(display, conf, eglConfigAttrib, value)) { - logger.log(level, "{0} = {1}", new Object[]{configName, value[0]}); + if ((val & EGL_OPENGL_ES2_BIT) != 0) { + return 2; } else { - logger.log(level, "Error getting {0} = {1}", - new Object[]{configName, egl.eglGetError()}); + return 1; } } + /** * log output with egl config details * @@ -179,25 +174,36 @@ public class AndroidConfigChooser implements EGLConfigChooser { * @param egl */ public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl, Level level) { - logger.log(level, "Logging EGLConfig Attributes"); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_RED_SIZE", EGL10.EGL_RED_SIZE); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_GREEN_SIZE", EGL10.EGL_GREEN_SIZE); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_BLUE_SIZE", EGL10.EGL_BLUE_SIZE); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_ALPHA_SIZE", EGL10.EGL_ALPHA_SIZE); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_STENCIL_SIZE", EGL10.EGL_STENCIL_SIZE); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_RENDERABLE_TYPE", EGL10.EGL_RENDERABLE_TYPE); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_SURFACE_TYPE", EGL10.EGL_SURFACE_TYPE); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_SAMPLE_BUFFERS", EGL10.EGL_SAMPLE_BUFFERS); - logEGLConfigAttrib(conf, display, egl, level, - "EGL_SAMPLES", EGL10.EGL_SAMPLES); + + 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)); } public int getClientOpenGLESVersion() { @@ -225,11 +231,17 @@ public class AndroidConfigChooser implements EGLConfigChooser { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE}; - egl.eglChooseConfig(display, configSpec, null, 0, num_config); + 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]; - egl.eglChooseConfig(display, configSpec, configs, numConfigs, num_config); + if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, num_config)) { + RendererUtil.checkEGLError(egl); + throw new AssertionError(); + } logger.fine("--------------Display Configurations---------------"); for (EGLConfig eGLConfig : configs) { @@ -251,12 +263,10 @@ public class AndroidConfigChooser implements EGLConfigChooser { */ private class ComponentSizeChooser extends BaseConfigChooser { - private int[] mValue; private ConfigType configType; protected int mSamples; public ComponentSizeChooser(ConfigType configType, int samples) { - mValue = new int[1]; mSamples = samples; this.configType = configType; } @@ -271,22 +281,22 @@ public class AndroidConfigChooser implements EGLConfigChooser { // first pass through config list. Try to find an exact match. for (EGLConfig config : configs) { - int r = findConfigAttrib(egl, display, config, - EGL10.EGL_RED_SIZE, 0); - int g = findConfigAttrib(egl, display, config, - EGL10.EGL_GREEN_SIZE, 0); - int b = findConfigAttrib(egl, display, config, - EGL10.EGL_BLUE_SIZE, 0); - int a = findConfigAttrib(egl, display, config, - EGL10.EGL_ALPHA_SIZE, 0); - int d = findConfigAttrib(egl, display, config, - EGL10.EGL_DEPTH_SIZE, 0); - int s = findConfigAttrib(egl, display, config, - EGL10.EGL_STENCIL_SIZE, 0); - int isMs = findConfigAttrib(egl, display, config, - EGL10.EGL_SAMPLE_BUFFERS, 0); - int nbMs = findConfigAttrib(egl, display, config, - EGL10.EGL_SAMPLES, 0); + 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_STENCIL_SIZE); + int isMs = eglGetConfigAttribSafe(egl, display, config, + EGL10.EGL_SAMPLE_BUFFERS); + int nbMs = eglGetConfigAttribSafe(egl, display, config, + EGL10.EGL_SAMPLES); if (inRange(r, configType.mr, configType.r) && inRange(g, configType.mg, configType.g) @@ -348,8 +358,8 @@ public class AndroidConfigChooser implements EGLConfigChooser { // failsafe. pick the 1st config with a 16 bit depth buffer. for (EGLConfig config : configs) { - int d = findConfigAttrib(egl, display, config, - EGL10.EGL_DEPTH_SIZE, 0); + int d = eglGetConfigAttribSafe(egl, display, config, + EGL10.EGL_DEPTH_SIZE); if (d >= 16) { return config; } @@ -362,15 +372,6 @@ public class AndroidConfigChooser implements EGLConfigChooser { private boolean inRange(int val, int min, int max) { return min <= val && val <= max; } - - private int findConfigAttrib(EGL10 egl, EGLDisplay display, - EGLConfig config, int attribute, int defaultValue) { - - if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { - return mValue[0]; - } - return defaultValue; - } } //DON'T REMOVE THIS, USED FOR UNIT TESTING FAILING CONFIGURATION LISTS. // private static class Config { diff --git a/engine/src/android/com/jme3/system/android/OGLESContext.java b/engine/src/android/com/jme3/system/android/OGLESContext.java index c51c4eca8..8e6961b69 100644 --- a/engine/src/android/com/jme3/system/android/OGLESContext.java +++ b/engine/src/android/com/jme3/system/android/OGLESContext.java @@ -48,7 +48,9 @@ import com.jme3.input.android.AndroidSensorJoyInput; import com.jme3.input.controls.SoftTextDialogInputListener; import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyMouseInput; +import com.jme3.renderer.RendererException; import com.jme3.renderer.android.OGLESShaderRenderer; +import com.jme3.renderer.android.RendererUtil; import com.jme3.system.*; import com.jme3.system.android.AndroidConfigChooser.ConfigType; import java.util.concurrent.atomic.AtomicBoolean; @@ -145,11 +147,16 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex } else { EGL10 egl = (EGL10) EGLContext.getEGL(); EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - + if (display == EGL10.EGL_NO_DISPLAY) { + throw new RendererException("No default EGL display is available"); + } + int[] version = new int[2]; - if (egl.eglInitialize(display, version) == true) { - logger.log(Level.INFO, "Display EGL Version: {0}.{1}", new Object[]{version[0], version[1]}); + if (!egl.eglInitialize(display, version)) { + RendererUtil.checkEGLError(egl); } + + logger.log(Level.INFO, "Display EGL Version: {0}.{1}", new Object[]{version[0], version[1]}); try { // Create a config chooser @@ -172,7 +179,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex view.setEGLConfigChooser(configChooser); view.getHolder().setFormat(configChooser.getPixelFormat()); } finally { - if (display != null) { + if (display != null && display != EGL10.EGL_NO_DISPLAY) { egl.eglTerminate(display); } }