Android OpenGL ES 3.0 対応 GPU を判定する方法

Android 4.3 から OpenGL ES 3.0 が使えるようになりました。
対応 GPU も順調に増えていますが、Android 4.3/4.4 が動く端末自体が
まだあまり多くありません。
GPU によっては OpenGL ES 3.0 の動作で問題が生じるものもあり、
互換性の意味でも OpenGL ES 2.0/3.0 両対応が望ましいでしょう。

iOS の方は比較的簡単で、今のところ 64bit 対応 (ARMv8 arm64) なら
OpenGL ES 3.0 にも対応しています。

Android 端末が OpenGL ES 3.0 に対応してるかどうか調べる方法は
ドキュメントに記載されています。

OpenGL ES : Checking OpenGL ES Version

ただしこの方法は以前も書いたように必ずしもうまく行きません。
1. 及びサンプルコードは OpenGL ES 3.0 の Context を作成し、
エラーかどうかで判断しています。
実際にいくつかの端末を調べてみると、GPU ドライバは未対応でも
エラーを返さない場合がほとんどです。

version != 2.0 で判定している GPU は OpenGL ES 1.1 の context を返しますし、
version < 2.0 で判定している GPU は OpenGL ES 2.0 の context になります。 2. の方法はうまく行きますが、一旦ダミーの Context を作成するため複雑です。
またこの判定方法は Android 限定です。

以下その具体的な方法

package jp.flatlib.gw;

import  android.opengl.GLES20;
import  android.os.Build;
import  android.graphics.SurfaceTexture;
import  javax.microedition.khronos.egl.EGL10;
import  javax.microedition.khronos.egl.EGLConfig;
import  javax.microedition.khronos.egl.EGLContext;
import  javax.microedition.khronos.egl.EGLDisplay;
import  javax.microedition.khronos.egl.EGLSurface;
import  android.util.Log;

public class GPUType {

    public static int       VersionCode= 0; // 200 or 300
    public static String    VersionString;
    public static String    RendererString;
    private static int      AdrenoID= 0;

    public static boolean   isGLES3()
    {
        return  VersionCode == 300;
    }

    public static boolean   isAdreno300s()
    {
        return  AdrenoID >= 300 && AdrenoID < 400;
    }

    private static int decodeAdrenoID( String gpu_name )
    {
        if( gpu_name.equals( "Adreno" ) || gpu_name.indexOf( "AMD Z430" ) >= 0 ){
            return  200;
        }else if( gpu_name.indexOf( "Adreno" ) >= 0 ){
            return  getAdrenoNumber( gpu_name );
        }
        return  0;
    }

    public static void  update()
    {

        EGL10   egl= (EGL10)EGLContext.getEGL();
        int[]   iparam= new int[8];

        if( Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ){
            VersionCode= 200;
            return;
        }

        // Initialize
        EGLDisplay  display= egl.eglGetDisplay( EGL10.EGL_DEFAULT_DISPLAY );
        egl.eglInitialize( display, iparam );

        // Config
        int[]   config_attr= { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE };
        final int   CONFIG_SIZE= 1;
        EGLConfig[] config_array= new EGLConfig[CONFIG_SIZE];
        egl.eglChooseConfig( display, config_attr, config_array, CONFIG_SIZE, iparam );
        EGLConfig   config= config_array[0];

        // Surface
        SurfaceTexture  surfaceTexture= new SurfaceTexture( 0 );    // API Level 11
        EGLSurface  surface= egl.eglCreateWindowSurface( display, config, surfaceTexture, null );

        // Context
        int[] context_attr= { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
        EGLContext  context= egl.eglCreateContext( display, config, EGL10.EGL_NO_CONTEXT, context_attr );

        // Make Current
        egl.eglMakeCurrent( display, surface, surface, context );

        // Query
        VersionString=  GLES20.glGetString( GLES20.GL_VERSION );
        RendererString= GLES20.glGetString( GLES20.GL_RENDERER );
        VersionCode= getGLVersionString( VersionString );
        AdrenoID= decodeAdrenoID( RendererString );


        // Special
        if( isAdreno300s() ){
            if( Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2 ){ // .. Android 4.3
                VersionCode= 200;   // GLES 2.0
            }
        }

        // Release
        egl.eglMakeCurrent( display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT );
        egl.eglDestroyContext( display, context );
        egl.eglDestroySurface( display, surface );

    }

    private static int getGLVersionString( String version_string )
    {
        int length= version_string.length();
        int ci= 0;
        for(; ci < length ; ci++ ){
            char    ch= version_string.charAt( ci );
            if( ch >= '0' && ch <= '9' ){
                break;
            }
        }
        int version= 0;
        int num_shift= 100;
        for(; num_shift > 0 && ci < length ;){
            char    ch= version_string.charAt( ci++ );
            if( ch >= '0' && ch <= '9' ){
                if( num_shift == 100 ){
                    if( version_string.charAt( ci ) == '.' ){
                        ci++;
                    }
                }
                version+= (ch - '0') * num_shift;
                num_shift/= 10;
            }else{
                break;
            }
        }
        return  version;
    }


    private static int getAdrenoNumber( String text )
    {
        int length= text.length();
        int num= 0;
        boolean is_num= false;
        for( int i= 0 ; i< length ; i++ ){
            char    ch= text.charAt( i );
            if( is_num ){
                if( ch >= '0' && ch <= '9' ){
                    num*= 10;
                    num+= ch - '0';
                }else{
                    break;
                }
            }else{
                if( ch >= '0' && ch <= '9' ){
                    is_num= true;
                    num= ch - '0';
                }
            }
        }
        return  num;
    }
}

GPUType.update() でデータを集めて isGLES3() で結果がわかります。

Android 3.0 API Level 11 以降でないと動きませんが、
Android 4.2 (API Level 17) 以前は OpenGL ES 2.0 と決め打ちできるため
特に問題ないはずです。

同じ理由で、EGL10 の代わりに EGL14 を使うこともできます。
EGL14 は API Level 17 以降の API です。

Android 4.3 + Adreno 320 の OpenGL ES 3.0 には問題があるので、
この組み合わせでは OpenGL ES 2.0 を返すようにしています。

関連エントリ
Android 4.4 Adreno 320 の OpenGL ES 3.0 と Version 判定
iPhone 5s の Apple A7 GPU
Adreno 320 の OpenGL ES 3.0 と Uniform Block
Nexus 7 (2013) の Adreno 320 と OpenGL ES 3.0 (Android 4.3)