createWindowSurface failed: EGL_BAD_MATCH?

2019-02-08 20:46发布

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?

2条回答
forever°为你锁心
2楼-- · 2019-02-08 21:20

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.

查看更多
虎瘦雄心在
3楼-- · 2019-02-08 21:32

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;
    }
}
查看更多
登录 后发表回答