2013/12/22
Android OpenGL ES 3.0 対応 GPU を判定する方法
Posted by: oga
at 23:58
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 限定です。
以下その具体的な方法
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)
対応 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)