java.lang.RuntimeException 在 PowerVR G6200 上使用 EGLConfigChooser

java.lang.RuntimeException with EGLConfigChooser on PowerVR G6200

在使用 PowerVR G6200 GPU 的设备上,例如 Sony Xperia M5 (E5603) 和小米 Redmi Note 3 (hennessy),创建用于使用 OpenGL ES 2 渲染的 EGL 上下文失败,而它适用于所有其他设备 I已测试:

java.lang.RuntimeException: 
  at android.opengl.GLSurfaceView$EglHelper.throwEglException (GLSurfaceView.java:1233)
  at android.opengl.GLSurfaceView$EglHelper.throwEglException (GLSurfaceView.java:1224)
  at android.opengl.GLSurfaceView$EglHelper.start (GLSurfaceView.java:1074)
  at android.opengl.GLSurfaceView$GLThread.guardedRun (GLSurfaceView.java:1447)
  at android.opengl.GLSurfaceView$GLThread.run (GLSurfaceView.java:1286)

配置选择器是这样实现的:

class CustomEGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
        EGLConfig [] configs = new EGLConfig[1];
        int [] num_config = new int[1];
        int [] attrib_list  = new int[] {
            EGL10.EGL_RED_SIZE, 8,
            EGL10.EGL_GREEN_SIZE, 8,
            EGL10.EGL_BLUE_SIZE, 8,
            EGL10.EGL_ALPHA_SIZE, 8,
            EGL10.EGL_DEPTH_SIZE, 24,
            EGL10.EGL_STENCIL_SIZE, 8,
            EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
            EGL10.EGL_NONE,
        };

        boolean res = egl.eglChooseConfig(display, attrib_list, configs, configs.length, num_config);
        if (res && num_config[0] > 0) {
            return configs[0];
        }   

        return null;
    }   
}   

GLSurfaceView 子类中使用配置选择器,如下所示:

public class GameView extends GLSurfaceView {
    public GameView (Context context, ClientConfig clientConfig) {
        super(context);
        setEGLContextClientVersion(2);
        setEGLConfigChooser(new CustomEGLConfigChooser());
    }
}

通过比较运行此代码的设备上的 EGL 配置列表 (Nexus 5x EGL configs) with a non-working device (Xiaomi Redmi Note 3 EGL configs),我发现虽然有匹配的配置,但它们有一个 属性 不同,即 EGL_RENDERABLE_TYPE.

第一个匹配的 Nexus 5x EGL 配置:

EGL_CONFIG_ID:  11
[...]
EGL_ALPHA_SIZE: 8
EGL_BLUE_SIZE:  8
EGL_GREEN_SIZE: 8
EGL_RED_SIZE:   8
EGL_DEPTH_SIZE: 24
EGL_STENCIL_SIZE:   8
[...]
EGL_RENDERABLE_TYPE:    OpenGL_ES OpenGL_ES_2 (5)

首先匹配的小米红米Note 3 EGL配置:

EGL_CONFIG_ID:  1
[...]
EGL_ALPHA_SIZE: 8
EGL_BLUE_SIZE:  8
EGL_GREEN_SIZE: 8
EGL_RED_SIZE:   8
EGL_DEPTH_SIZE: 24
EGL_STENCIL_SIZE:   8
[...]
EGL_RENDERABLE_TYPE:    OpenGL_ES (1)

请注意,Nexus 5x 支持 EGL_RENDERABLE_TYPEEGL_OPENGL_ES2_BIT(此外还支持具有相同配置的 EGL_OPENGL_ES_BIT),所以我们很幸运,现有设备恰好支持这两种可渲染类型。

小米红米Note 3还有一个支持ES2渲染类型的配置,但这并不是第一个匹配的配置:

EGL_CONFIG_ID:  23
[...]
EGL_ALPHA_SIZE: 8
EGL_BLUE_SIZE:  8
EGL_GREEN_SIZE: 8
EGL_RED_SIZE:   8
EGL_DEPTH_SIZE: 24
EGL_STENCIL_SIZE:   8
[...]
EGL_RENDERABLE_TYPE:    OpenGL_ES_2 (4)

尝试将第一个(配置 ID 1)与 ES2 渲染上下文一起使用显然不起作用,因为该配置不支持 ES2 渲染。解决方法是明确请求可呈现类型作为 EGL 配置选择器的一部分,像这样修改问题中的示例:

        int [] attrib_list  = new int[] {
            EGL10.EGL_RED_SIZE, 8,
            EGL10.EGL_GREEN_SIZE, 8,
            EGL10.EGL_BLUE_SIZE, 8,
            EGL10.EGL_ALPHA_SIZE, 8,
            EGL10.EGL_DEPTH_SIZE, 24,
            EGL10.EGL_STENCIL_SIZE, 8,
            EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
            EGL10.EGL_RENDERABLE_TYPE, EGL10. EGL_OPENGL_ES2_BIT,
            EGL10.EGL_NONE,
        };

请注意,在只需要请求 RGBA + depth/stencil 值的情况下,另一种选择是使用 setEGLConfigChooser(int, int, int, int, int, int) 而不是自定义实现,这将负责请求EGL_RENDERABLE_TYPE 基于 setEGLContextClientVersion().

中设置的值