更新:我找到了解决这个问题的方法,实际上它非常简单。
首先:Android默认的 EGLConfigChooser
实现在某些设备上做出了不好的决策,特别是旧版 Android 设备似乎会遇到 EGL_BAD_MATCH
问题。在我的调试过程中,我还发现这些旧设备具有相当有限的可用 OpenGL ES 配置。
"bad match" 问题的原因不仅仅是 GLSurfaceView 的像素格式与 OpenGL ES 颜色位深设置之间的不匹配。总体而言,我们需要处理以下问题:
- OpenGL ES API 版本不匹配
- 请求的目标表面类型不匹配
- 所请求的颜色位深不能在 Surface View 上呈现
当涉及到解释 OpenGL ES API 时, Android 开发文档非常缺乏。因此,阅读 Khronos.org 上的原始文档非常重要。尤其是关于 eglChooseConfig 的文档页在这里非常有帮助。
为了解决上述问题,您必须确保指定以下最小配置:
EGL_RENDERABLE_TYPE
必须与您正在使用的 OpenGL ES API 版本匹配。在 OpenGL ES 2.x 的情况下,您必须将该属性设置为 4
(请参见egl.h
)
EGL_SURFACE_TYPE
应设置为 EGL_WINDOW_BIT
当然,您还需要设置一个 OpenGL ES 上下文,以提供正确的颜色、深度和模板缓冲区设置。
不幸的是,在直接方式下,无法挑选这些配置选项。我们必须从任何给定设备上可用的内容中进行选择。这就是为什么需要实现自定义的 EGLConfigChooser
,它遍历可用配置集列表并选择最适合符合给定标准的那个。
无论如何,我为这样的配置选择器编写了一个示例实现:
public class MyConfigChooser implements EGLConfigChooser {
final private static String TAG = "MyConfigChooser";
final private static int EGL_OPENGL_ES2_BIT = 4;
private static int[] mMinimumSpec = {
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_NONE,
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;
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) {
return null;
}
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(display, mMinimumSpec, configs,
numConfigs, arg);
EGLConfig chosen = chooseConfig(egl, display, configs);
if(chosen == null) {
throw new RuntimeException(
"Could not find a matching configuration out of "
+ configs.length + " available.",
configs);
}
return chosen;
}
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;
}
}
EGLConfig [] config
)的顺序,而且我相信这可能完全是特定设备的。如果你想确保,你可以改变config chooser,以便考虑颜色配置的上限。我的样本chooser并不那么严格,它只将用户给定的位深度视为最低要求,并愉快地选择任何超过该要求的东西。 - tiguchiRGB_888
的EGL上下文转换为渲染目标表面视图的RGB_565
。在我的调试会话中,我发现EGL_TRANSPARENT_TYPE
标志实际上是主要的问题制造者(至少对于我的情况是这样)。如果选择器没有将值限制为EGL_NONE
,则可能会意外选择需要表面视图本身支持半透明像素才能让底部的视图“透过”显示。这通常不是默认设置,在一些旧设备上甚至不被支持。 - tiguchi