OpenGL のエラー状態を調べるには glGetError() を使います。
3~5種類程度のエラーコードが得られるので、マニュアルを頼りに
API 毎に発生条件を照らし合わせていきます。
どのタイミングで発生したのか特定するには、OpenGL の命令呼び出し毎に
glGetError() をチェックしなければなりません。
● glGetError() の判定
glActiveTexture( GL_TEXTURE0 + 32 ); GLenum error_code= glGetError(); if( error_code != GL_NO_ERROR ){ // ERROR }
↑これを毎回手で行うのは大変なので、何らかの形で自動化します。
現在は、下記のようなヘッダを作って対処しています。
// GLErrorLayer_ES2.h // __gl2_h_ inline void ActiveTexture( GLenum texture ) { glActiveTexture( texture ); FL_GL_ERROR_TRAP( "glActiveTexture" ); } inline void AttachShader( GLuint program, GLuint shader ) { glAttachShader( program, shader ); FL_GL_ERROR_TRAP( "glAttachShader" ); } ~ inline GLint GetAttribLocation( GLuint program, const GLchar * name ) { GLint ret= glGetAttribLocation( program, name ); FL_GL_ERROR_TRAP( "glGetAttribLocation" ); return ret; } ~
GLES2/gl2.h, GLES3/gl3.h, GL4/glcorearb.h などのヘッダファイルを元に
スクリプトで自動生成しています。
下記のように include して使います。
// GLErrorLayer.h namespace el { #if flDEBUG # define FL_GL_ERROR_TRAP( func_name ) gw::ErrorHandle( func_name ) #else # define FL_GL_ERROR_TRAP( func_name ) #endif #include "gw/entry/GLErrorLayer_ES2.h" }
↑コンパイル時にエラー判定を行うかどうか選択できます。
実際のエラー判定は下記のような関数です。
bool ErrorHandle( const char* name ) { GLenum error_code= glGetError(); if( error_code == GL_NO_ERROR ){ return true; } do{ const char* msg= ""; switch( error_code ){ case GL_INVALID_ENUM: msg= "INVALID_ENUM"; break; case GL_INVALID_VALUE: msg= "INVALID_VALUE"; break; case GL_INVALID_OPERATION: msg= "INVALID_OPERATION"; break; case GL_OUT_OF_MEMORY: msg= "OUT_OF_MEMORY"; break; case GL_INVALID_FRAMEBUFFER_OPERATION: msg= "INVALID_FRAMEBUFFER_OPERATION"; break; default: msg= "Unknown"; break; } FL_ERROR( "GLErrorLayer:ERROR:%04x'%s' %s\n", error_code, msg, name ); error_code= glGetError(); }while( error_code != GL_NO_ERROR ); FL_ABORT(); return false; }
利用時は el:: をつけて下記のような感じです。
el::ActiveTexture( GL_TEXTURE );
これはエラー判定の一例ですが、楽をしたければそれなりの準備が必要で、
結構手間もかかります。
生成したヘッダは下記の場所に置いてみました。
・OpenGL ES 2.0 : GLErrorLayer_ES2.h
・OpenGL ES 3.0 : GLErrorLayer_ES3.h
・OpenGL 4.4 : GLErrorLayer_GL4.h
● Debug Output (OpenGL 4.3)
OpenGL のエラー出力にはいくつかの例外があります。
例えば Shader API では、Compiler や Linker のエラー内容をプログラマ向けの
テキスト情報で受け取ることができます。
同じように OpenGL 4.3 では、API のエラーメッセージを
わかりやすい形↓で受け取れるようになっています。
// GeForce GTX 650 (OpenGL 4.4 beta) GL_INVALID_VALUE error generated. Element exceeds maximum supported number of combined texture image units. GL_INVALID_ENUM error generated.enum is invalid; expected GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_TEXTURE_WRAP_R, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_GENERATE_MIPMAP, GL_TEXTURE_COMPARE_MODE, etc. (19 others).
// RADEON HD 7750 (OpenGL 4.3 beta) glActiveTexture has generated an error (GL_INVALID_ENUM) glTexParameteri parameterhas an invalid enum '0x285a' (GL_INVALID_ENUM)
Debug Output はちょうど Direct3D で言えば Debug Layer に相当します。
Direct3D 10 以降は GPU が汎用化されてできることが増えたのですが、
それでも GPU は CPU と違ってリソースの扱いでもさまざまな制約があります。
ちょっとした細かい設定やフラグの組み合わせなど、気付きにくいようなミスも
D3D Debug Layer は (少々うるさいくらいに) 報告してくれました。
デバッグだけでなく機能の理解にも役に立ちました。
OpenGL の場合はいくつかの準備が必要です。
1. Context 作成時に WGL_CONTEXT_FLAGS_ARB に WGL_CONTEXT_DEBUG_BIT_ARB を設定
2. glDebugMessageCallbackARB() で callback 関数を登録する
RADEON の場合下記のように GL_VERSION 文字列も変化します。
GL_VERSION: 4.3.12438 Core Profile/Debug Context 13.200.0.0 GL_RENDERER: AMD Radeon HD 7700 Series GL_VENDOR: ATI Technologies Inc. GL_SHADING_LANGUAGE_VERSION: 4.30
static int att_command[]= { WGL_CONTEXT_MAJOR_VERSION_ARB, 4, WGL_CONTEXT_MINOR_VERSION_ARB, 3, WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0 }; hrc= wglCreateContextAttribsARB( hDC, 0, att_command );
wglMakeCurrent() で OpenGL API が使えるようになったら Callback 関数を
登録します。
glDebugMessageCallback( DebugCallback, reinterpret_cast( this ) ); glEnable( GL_DEBUG_OUTPUT );
Debug Context はデフォルトで glEnable( GL_DEBUG_OUTPUT ) になるので
2行目は無くても構いません。
OpenGL の命令でエラーが発生すると Callback 関数が呼ばれます。
void GWModule::DebugCallbackFunc( GLenum source, GLenum type, GLuint eid, GLenum severity, GLsizei length, const GLchar* message, const void* user_param ) { ~ FL_LOG( "%s\n", message ); }
手間もかからずにエラー内容も発生場所もわかるので、非常に便利になりました。
● OpenGL 4.2
GL_ARB_debug_output があれば同様のエラー情報を利用することができます。
関数名の最後に ARB が付くだけです。
RADEON HD 6570M OpenGL 4.2 で動作を確認できました。
● OpenGL ES 3.0
Mali-T604 では、Android 4.3 の OpenGL ES 3.0 利用時に同様の機能
GL_KHR_debug があります。
いろいろ試したのですが、eglGetProcAddress() でも
glDebugMessageCallbackKHR() などの関数を取ることができませんでした。
この辺りはもう少し調べたいと思っています。
関連エントリ
・OpenGL のはじめ方