11

the version android is 2.2.1 the device is a samsung galaxy II the full crash log is:

java.lang.RuntimeException: createWindowSurface failed: EGL_BAD_MATCH
at android.opengl.GLSurfaceView$EglHelper.throwEglException(GLSurfaceView.java:1077)
at android.opengl.GLSurfaceView$EglHelper.createSurface(GLSurfaceView.java:981)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1304)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1116)

this is the relevant code to the crash:

@Override 
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                         WindowManager.LayoutParams.FLAG_FULLSCREEN);
    glView = new GLSurfaceView(this);
    glView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0);
    glView.setRenderer(this);
    setContentView(glView);
    \\etc..............}

i used setEGLConfigChooser() because the app would crash on API-17 if it wasnt in there so for this specific device that it is crashing on i been looking around and it has something to do with the PixelFormat for the device.

What im wondering is how can i use some code so this will not crash on the samsung galaxy II android version 2.2.1, i cant test this in an emulator and i dont have the device to test it in, i just need for sure code and im not sure how to change it up?

JRowan
  • 6,824
  • 8
  • 40
  • 59

2 Answers2

10

Update: I found a way to work around this issue and actually it is fairly straightforward.

First of all: Android's default EGLConfigChooser implementation makes bad decisions on some devices. Especially the older Android devices seem to suffer this EGL_BAD_MATCH issue. During my debugging sessions I also discovered that those older troublemaker devices had quite a limited set of available OpenGL ES configurations.

The cause of this "bad match" problem is more than just a mismatch between the GLSurfaceView's pixel format and the color bit depth settings of OpenGL ES. Overall we have to deal with the following issues:

  • A mismatch of the OpenGL ES API version
  • A mismatch of the requested target surface type
  • The requested color bit depth cannot be rendered on the surface view

The Android developer documentation is severely lacking when it comes to explaining the OpenGL ES API. It is therefore important to read the original documentation over at Khronos.org. Especially the doc page about eglChooseConfig is helpful here.

In order to remedy above listed problems you have to make sure to specify the following minimum configuration:

  • EGL_RENDERABLE_TYPE must match the OpenGL ES API version you are using. In the likely case of OpenGL ES 2.x you must set that attribute to 4(see egl.h)
  • EGL_SURFACE_TYPE should have the EGL_WINDOW_BIT set

And of course you also want to set up an OpenGL ES context that provides you with the correct color, depth and stencil buffer settings.

Unfortunately it is not possible to cherry-pick these configuration options in a straightforward way. We have to choose from whatever is available on any given device. That's why it is necessary to implement a custom EGLConfigChooser, that goes through the list of available configuration sets and picks the most suitable one that matches best the given criteria.

Anyway, I whipped up a sample implementation for such a config chooser:

public class MyConfigChooser implements EGLConfigChooser {
    final private static String TAG = "MyConfigChooser";

    // This constant is not defined in the Android API, so we need to do that here:
    final private static int EGL_OPENGL_ES2_BIT = 4;

    // Our minimum requirements for the graphics context
    private static int[] mMinimumSpec = {
            // We want OpenGL ES 2 (or set it to any other version you wish)
            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,

            // We want to render to a window
            EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,

            // We do not want a translucent window, otherwise the
            // home screen or activity in the background may shine through
            EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_NONE, 

            // indicate that this list ends:
            EGL10.EGL_NONE
    };

    private int[] mValue = new int[1];
    protected int mAlphaSize;
    protected int mBlueSize;
    protected int mDepthSize;
    protected int mGreenSize;
    protected int mRedSize;
    protected int mStencilSize;

    /**
    * The constructor lets you specify your minimum pixel format,
    * depth and stencil buffer requirements.
    */
    public MyConfigChooser(int r, int g, int b, int a, int depth, int 
                        stencil) {
        mRedSize = r;
        mGreenSize = g;
        mBlueSize = b;
        mAlphaSize = a;
        mDepthSize = depth;
        mStencilSize = stencil;
    }

    @Override
    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
        int[] arg = new int[1];
        egl.eglChooseConfig(display, mMinimumSpec, null, 0, arg);
        int numConfigs = arg[0];
        Log.i(TAG, "%d configurations available", numConfigs);

        if(numConfigs <= 0) {
            // Ooops... even the minimum spec is not available here
            return null;
        }

        EGLConfig[] configs = new EGLConfig[numConfigs];
        egl.eglChooseConfig(display, mMinimumSpec, configs,    
            numConfigs, arg);

        // Let's do the hard work now (see next method below)
        EGLConfig chosen = chooseConfig(egl, display, configs);

        if(chosen == null) {
            throw new RuntimeException(
                    "Could not find a matching configuration out of "
                            + configs.length + " available.", 
                configs);
        }

        // Success
        return chosen;
    }

   /**
    * This method iterates through the list of configurations that 
    * fulfill our minimum requirements and tries to pick one that matches best
    * our requested color, depth and stencil buffer requirements that were set using 
    * the constructor of this class.
    */
    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
            EGLConfig[] configs) {
        EGLConfig bestMatch = null;
        int bestR = Integer.MAX_VALUE, bestG = Integer.MAX_VALUE, 
            bestB = Integer.MAX_VALUE, bestA = Integer.MAX_VALUE, 
            bestD = Integer.MAX_VALUE, bestS = Integer.MAX_VALUE;

        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);

            if(r <= bestR && g <= bestG && b <= bestB && a <= bestA
                    && d <= bestD && s <= bestS && r >= mRedSize
                    && g >= mGreenSize && b >= mBlueSize 
                    && a >= mAlphaSize && d >= mDepthSize 
                    && s >= mStencilSize) {
                bestR = r;
                bestG = g;
                bestB = b;
                bestA = a;
                bestD = d;
                bestS = s;
                bestMatch = config;
            }
        }

        return bestMatch;
    }

    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;
    }
}
tiguchi
  • 5,392
  • 1
  • 33
  • 39
  • Assume I want RGB_565. Will this choose RGB_888 over RGB_965(if it exists) Assuming RGB_888 comes first in `configs` list? – kiranpradeep Nov 29 '14 at 15:34
  • @Kiran That's a good question. I fear that depends on the order of the list of OpenGL ES configuration sets (namely the array `EGLConfig[] config`), and I believe that may be completely device-specific. If you want to make sure you could alter the config chooser in order to take upper bounds for the color configuration into account. My sample chooser is not that restrictive, it only considers the user-given bit depths as minimum requirements and happily picks anything that goes beyond. – tiguchi Nov 29 '14 at 16:09
  • @Kiran BTW, I believe that Android is automatically able to convert an `RGB_888` EGL context into `RGB_565` of the render target surface view. In my debugging sessions I figured out that the `EGL_TRANSPARENT_TYPE` flag was actually the main trouble maker (at least for my case). If a chooser does not restrict the value to `EGL_NONE` it is possible that a configuration is accidentally selected that requires the surface view itself to support translucent pixels in order to let views underneath "shine through". That's usually not the default setting and on some older devices simply not supported. – tiguchi Nov 29 '14 at 16:30
4

I don't have the reputation score to add a comment yet, or else I would have put a brief comment on Nobu Games' answer. I encountered this same EGL_BAD_MATCH error and their answer helped put me on the right path. Instead, I have to create a separate answer.

As Nobu Games mentions, there appears to be a mismatch between the GLSurfaceView's PixelFormat and the pixel format parameters passed to setEGLConfigChooser(). In my case, I was asking for RGBA8888 but my GLSurfaceView was RGB565. This caused the EGL_BAD_MATCH error later on within my initialization.

The enhancement to their answer is that you can get the desired PixelFormat for the window and use it to dynamically choose an EGL context.

To make my code as generic as possible, I changed the GLSurfaceView to take in an additional parameter -- the pixel format of the display. I get this from my activity by calling:

getWindowManager().getDefaultDisplay().getPixelFormat();

I pass this value down to the GLSurfaceView and then extract the optimal bit depths for each of RGBA like this:

if (pixelFormatVal > 0) {

    PixelFormat info = new PixelFormat();
    PixelFormat.getPixelFormatInfo(pixelFormatVal, info);

    if (PixelFormat.formatHasAlpha(pixelFormatVal)) {

        if (info.bitsPerPixel >= 24) {
            m_desiredABits = 8;
        } else {
            m_desiredABits = 6;  // total guess
        }

    } else {
        m_desiredABits = 0;
    }

    if (info.bitsPerPixel >= 24) {
        m_desiredRBits = 8;
        m_desiredGBits = 8;
        m_desiredBBits = 8;
    } else if (info.bitsPerPixel >= 16) {
        m_desiredRBits = 5;
        m_desiredGBits = 6;
        m_desiredRBits = 5;
    } else {
        m_desiredRBits = 4;
        m_desiredGBits = 4;
        m_desiredBBits = 4;
    }

} else {
    m_desiredRBits = 8;
    m_desiredGBits = 8;
    m_desiredBBits = 8;
}

I then pass these values down to my config chooser. This code works for me on a RGB565 device as well as a RGBA8888 device.

My assumption is that the vendor has chosen the default for a reason and that it will give the most performant results. Of course I have nothing to back that statement up, but it is the strategy I'm going with.

markmanca
  • 163
  • 1
  • 5
  • Thanks for the detailed answer! This looked promising, but unfortunately, it looks like getPixelFormat() is now deprecated, and will return RGBA_8888 every time, making it far less useful. (See: http://developer.android.com/reference/android/view/Display.html#getPixelFormat()). I'm still looking for a good solution to this... – gkanwar Jul 14 '14 at 16:36