针对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配置。
|