針對java.lang.IllegalArgumentException: No config chosen異常,網絡上流行的修改辦法是:
在游戲端,調用gLSurfaceView.setEGLConfigChooser(8 8, 8, 8, 16, 0)。
這樣修改,到底修改了什么,如何起作用的?下面分析下源碼,看看這個問題。
EGL 是 OpenGLES 和底層 Native 平台視窗系統之間的接口。對EGL的使用,一般是初始化后,選擇恰當的配置。
下面是選擇一個 EGL 配置的函數原型:
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint * num_config);
參數 attrib_list 指定了選擇配置時需要參照的屬性。參數 configs 將返回一個按照 attrib_list 排序的平台有效的所有 EGL framebuffer 配置列表。參數 config_size 指定了可以返回到 configs 的總配置個數。參數 num_config 返回了實際匹配的配置總數。
1.異常發生的現場如下:
<BaseConfigChooser類>
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { int[] num_config = new int[1]; //第一次調用eglChooseConfig獲取有效配置數,mConfigSpec為參考屬性,在構造函數中賦值 if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config)) { throw new IllegalArgumentException("eglChooseConfig failed"); } int numConfigs = num_config[0]; if (numConfigs <= 0) { throw new IllegalArgumentException( "No configs match configSpec"); } EGLConfig[] configs = new EGLConfig[numConfigs]; //第二次調用eglChooseConfig獲取有效配置列表 if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config)) { throw new IllegalArgumentException("eglChooseConfig#2 failed"); } //調用另一個重載的chooseConfig函數(為抽象方法,在子類中實現),根據給定的r,g,b,a,depth,stencil選擇EGL配置 EGLConfig config = chooseConfig(egl, display, configs); if (config == null) { //拋出java.lang.IllegalArgumentException: No config chosen異常 throw new IllegalArgumentException("No config chosen"); } return config; }
2.下面來看看chooseConfig(egl, display, configs)何時會返回NULL
<ComponentSizeChooser類繼承自BaseConfigChooser>
private class ComponentSizeChooser extends BaseConfigChooser { //構造函數 public ComponentSizeChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize) { //構造父類,即BaseConfigChooser,設置對應的mConfigSpec super(new int[] { EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, EGL10.EGL_BLUE_SIZE, blueSize, EGL10.EGL_ALPHA_SIZE, alphaSize, EGL10.EGL_DEPTH_SIZE, depthSize, EGL10.EGL_STENCIL_SIZE, stencilSize, EGL10.EGL_NONE}); mValue = new int[1]; mRedSize = redSize; mGreenSize = greenSize; mBlueSize = blueSize; mAlphaSize = alphaSize; mDepthSize = depthSize; mStencilSize = stencilSize; } public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { for (EGLConfig config : configs) { int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0); int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0); if ((d >= mDepthSize) && (s >= mStencilSize)) { 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); if ((r == mRedSize) && (g == mGreenSize) && (b == mBlueSize) && (a == mAlphaSize)) { return config; } } } return null; }
該函數遍歷所有配置,找到第一個滿足如下條件的配置:
①Depth和Stencil不小於給定值
②R,G,B,Alpha等於給定值
當沒有滿足上述條件的EGL配置時,返回NULL。
這些給定值從和而來?這就是setEGLConfigChooser的工作
3.setEGLConfigChooser都干了些啥
<GLSurfaceView類>
public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize) { setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize)); }
該函數根據給定的r,g,b,a,depth,stencil值創建ComponentSizeChooser實例。ComponentSizeChooser::chooseConfig函數會用到這些值。
從以上源碼可知,
調用gLSurfaceView.setEGLConfigChooser(8 8, 8, 8, 16, 0),
GLSurfaceView 將根據(r,g,b,a,depth,stencil)-(8 8, 8, 8, 16, 0)去選擇EGL配置。