D3D Shader/OpenGL」カテゴリーアーカイブ

OpenGL 4.x Program Pipeline Object (Separate Shader Object)

OpenGL のシェーダーは Direct3D と違い、API レベルでシンボル管理が行われます。

Direct3D の Native な API では、Vertex Shader, Pixel Shader,
Geometry Shader などの、各シェーダー毎に Object が分かれています。
ステートやバインド情報もそれぞれ別で、レンダリングのタイミングで
必要な情報を Direct3D の DeviceContext に割り当てます。
Constant (Unifrom) の管理も各シェーダー毎に別空間です。

例えば Camera 座標など、Vertex Shader, Pixel Shader 両方が
アクセセスするパラメータがあった場合は
PSSetConstantBuffers(), VSSetConstantBuffers()
両方に設定しなければなりません。
これはプログラマの仕事です。

OpenGL の場合 Vertex Shader, Fragment Shader, Geometry Shader など、
描画に必要なシェーダーを集めて最初に Program Object を作っておきます。
同時に Uniform や Block 等のシンボルの共有化が行われており、
どのシェーダーから参照されているのか情報が保持されます。

各シンボルの参照状態は OpenGL 4.3 以降は
glGetProgramResourceiv( GL_REFERENCED_BY_*_SHADER )
を使って調べることが出来ます。

プログラマはどのシェーダーで使われているのか意識することなく
単一のシンボルに対して設定すればよいだけです。
もちろん Vertex Shader, Fragment Shader 両方から参照されているなら
両方とも同じ値に更新されます。

OpenGL 4.1 から利用可能となった Program Pipeline Object は、
このような OpenGL Program Object の一括管理とは正反対のものです。
OpenGL 上で Direct3D のような、より低レベルだけど自由度の高い
シェーダー管理を可能にします。

GPU 周りの進化は非常に速いので、単一の仕組みだけでは様々な
要求に応えることが難しくなってきているのかもしれません。

●Shader の組み合わせ

レンダリングに使われるシェーダーのバリエーションはかなり膨大な量になります。
手で管理することは不可能なので、描画用のエンジン開発では
最初にシェーダー管理の仕組みを構築することになります。

ある程度の組み合わせに融通は利くので、用途に応じて
Pixel Shader (Fragment Shader) だけ変えたり、
部分的に使いまわしたりすることがありました。

OpenGL の場合 Shader Object レベルでは組み合わせを作ることが出来ます。
実際の利用時は Program Object が必要となるので、組み合わせの数だけ
Program Object が出来上がります。

Program Pipeline Object は、既存の OpenGL API の仕組みを利用したまま
動的に Shader を入れ替えられるようにしています。

 1. Shader 単独の Program Object を作る
 2. Pipeline Object にそれぞれ登録する
 3. 描画時に glUseProgram() の代わりに glBindProgramPipeline() を使う

//● 通常のシェーダーの場合

// Vertex Shader
GLuint vsh_obj= glCreateShader( GL_VERTEX_SHADER );
glShaderSource( vsh_obj, 1, &vsh_source_text );
glCompileShader( vsh_obj );

// Fragment Shader
GLuint fsh_obj= glCreateShader( GL_FRAGMENT_SHADER );
glShaderSource( fsh_obj, 1, &fsh_source_text );
glCompileShader( fsh_obj );

// Program Object を作成 (Link)
GLuint program_obj= glCreateProgram();
glAttachShader( program_obj, vsh_obj );
glAttachShader( program_obj, fsh_obj );
glLinkProgram( program_obj );

// 利用時
glUseProgram( program_obj );

Pipeline Object を利用する場合は下記の通り。

//● Pipeline Object を使う場合

// Vertex Shader
GLuint vsh_obj= glCreateShader( GL_VERTEX_SHADER );
glShaderSource( vsh_obj, 1, &vsh_source_text );
glCompileShader( vsh_obj );

// 単独の Vertex Shader Program に変換
GLuint vsh_program= glCreateProgram();
glProgramParameteri( vsh_program, GL_PROGRAM_SEPARABLE, GL_TURE );
glAttachShader( vsh_program, vsh_obj );
glLinkProgram( vsh_program );


// Fragment Shader
GLuint fsh_obj= glCreateShader( GL_FRAGMENT_SHADER );
glShaderSource( fsh_obj, 1, &fsh_source_text );
glCompileShader( fsh_obj );

// 単独の Fragment Shader Program に変換
GLuint fsh_program= glCreateProgram();
glProgramParameteri( fsh_program, GL_PROGRAM_SEPARABLE, GL_TURE );
glAttachShader( fsh_program, fsh_obj );
glLinkProgram( fsh_program );


// Pipeline Object
GLuint pipeline= 0;
glGenPipelineObjects( 1, &pipeline );
glGenProgramPipelines( 1, &pipeline );
glUseProgramStages( pipeline, GL_VERTEX_SHADER_BIT, vsh_program );
glUseProgramStages( pipeline, GL_FRAGMENT_SHADER_BIT, fsh_program );


// 利用時
glUseProgram( 0 );
glBindProgramPipeline( pipeline );

↑一見複雑ですが、Program Object を作るための便利な命令↓が増えています。

//● Pipeline Object を使う場合
GLuint vsh_program= glCreateShaderProgramv( GL_VERTEX_SHADER, 1, &vsh_source_text );
GLuint psh_program= glCreateShaderProgramv( GL_FRAGMENT_SHADER, 1, &fsh_source_text );

// Pipeline Object
GLuint pipeline= 0;
glGenPipelineObjects( 1, &pipeline );
glGenProgramPipelines( 1, &pipeline );
glUseProgramStages( pipeline, GL_VERTEX_SHADER_BIT, vsh_program );
glUseProgramStages( pipeline, GL_FRAGMENT_SHADER_BIT, fsh_program );


// 利用時
glUseProgram( 0 );
glBindProgramPipeline( pipeline );

↑簡単になりました。

●情報アクセス

Pipeline Object は複数の Program Object を取りまとめる器に過ぎず、
いつでも Program Object の追加と削除ができます。

Link のように大掛かりなデータ共有は行わず、入出力など最低限の
マッチングだけ行います。
入出力のマッチングはシンボル名または Location を元に行われるようです。

Program Object が持っていた情報もシェーダー個別に扱うことになります。
例えば Attribute の Location、Default Uniform Block などがそうです。

もともと Program Object のアクセス API は OpenGL の他の API と構造が
異なっており、多くのケースで Bind を必要としていませんでした。
引数として Program Object を取るものが多いのですが、唯一
glUniform() 系の API だけが glUseProgram() に依存しています。

方法は 3通り

1. Default Uniform Block を使わない

2. 直接 Program Object を引数にとる新しい API を使う

glUseProgram( vsh_program );
glUniform4fv( location, 1, vect );
 ↓
glProgramUniform4fv( vsh_program, location, 1, vect );

3. glActiveShaderProgram() を使う

glActiveShaderProgram() を使うと従来の Uniform 命令を使うことができます。

glUseProgram( program );
glUniform4fv( location, 1, vect );
 ↓
glUseProgram( 0 );
glBindProgramPipeline( pipeline );
glActiveShaderProgram( pipeline, vsh_program );
glUniform4fv( location, 1, vect );

●Bind Point

Pipeline Object は今までの OpenGL API にできるだけ影響を与えないように
作られています。
Bind Point は新たに追加されており、他のステートにも干渉しません。

例えば glUseProgram() が有効な場合、glUseProgram() が優先され
glBindProgramPipeline() は無視します。
Pipeline Object を有効にするにはこれまでの例で示したように
glUseProgram( 0 ) が必要です。

●実際のシェーダーと最適化の違い

Pipeline Object を使う場合、gl_Position も自分で定義しておきます。

#version 430

// Vertex Shader

uniform mat4	PView;
uniform mat4	World;
uniform vec4	UVOffset;

layout(location=0) in vec3 POSITION;
layout(location=1) in vec3 NORMAL;
layout(location=2) in vec2 TEXCOORD;

out vec3 onormal;
out vec2 otexcoord;

out gl_PerVertex {
    vec4 gl_Position;
};

void main()
{
    mat4  pview= World * PView;
    vec4  opos= vec4( POSITION.xyz, 1.0 ) * pview;
    opos.z= 2.0 * opos.z - opos.w;
    gl_Position= opos;
    onormal= NORMAL.xyz * mat3x3( World );
    otexcoord= TEXCOORD.xy + UVOffset.xy;
}
#version 430

// Fragment Shader

out vec4  out_FragColor;
uniform vec4   UVOffset;

in vec3	  onormal;
in vec2   otexcoord;
layout(binding=0) uniform sampler2D  ColorMap;

void main()
{
    float   diff= clamp( dot( vec3( 0, 0, -1 ), normalize( onormal.xyz ) ), 0.0, 1.0 );
    vec2    texcoord= otexcoord.xy - UVOffset.xy;
    vec4    color= texture( ColorMap, texcoord.xy );
    out_FragColor.xyz= color.xyz * diff;
    out_FragColor.w= 1.0;
}

UVOffset は Vertex と Fragment で同名です。
Pipeline Object を使った場合は異なる値に設定することが可能です。

上のコードのシンボル情報を調べてみます。
前回の API を使って情報を取り出したのが下記の結果です。
GeForce もほぼ同じでした。

// ●テクスチャあり Pipeline Object

// RADEON 4.3 : Vertex Shader
 Prg: 0011   Del=0 Link=1 Vali=1 Separable=1
Resource [Uniform]
  0: FLOAT_MAT4  Array=0 Loc=0 V_____  "PView"
  1: FLOAT_VEC4  Array=0 Loc=1 V_____  "UVOffset"
  2: FLOAT_MAT4  Array=0 Loc=2 V_____  "World"
Resource [Input]
  0: FLOAT_VEC3  Array=0       V_____  "NORMAL"
  1: FLOAT_VEC3  Array=0       V_____  "POSITION"
  2: FLOAT_VEC2  Array=0       V_____  "TEXCOORD"
Resource [Output]
  0: FLOAT_VEC4  Array=0       V_____  "gl_PerVertex.gl_Position"
  1: FLOAT_VEC3  Array=0       V_____  "onormal"
  2: FLOAT_VEC2  Array=0       V_____  "otexcoord"


// RADEON 4.3 : Fragment Shader
 Prg: 0012   Del=0 Link=1 Vali=1 Separable=1
Resource [Uniform]
  0: FLOAT_VEC4  Array=0 Loc=0 ____F_  "UVOffset"
  1: SAMPLER_2D  Array=0 Loc=1 ____F_  "ColorMap"
Resource [Input]
  0: FLOAT_VEC3  Array=0       ____F_  "onormal"
  1: FLOAT_VEC2  Array=0       ____F_  "otexcoord"
Resource [Output]
  0: FLOAT_VEC4  Array=0       ____F_  "out_FragColor"

次に Fragment Shader の texture 命令を削除してみます。

#version 430

// Fragment Shader

out vec4  out_FragColor;
uniform vec4   UVOffset;

in vec3	  onormal;
in vec2   otexcoord;

layout(binding=0) uniform sampler2D  ColorMap;

void main()
{
    float   diff= clamp( dot( vec3( 0, 0, -1 ), normalize( onormal.xyz ) ), 0.0, 1.0 );
    vec2    texcoord= otexcoord.xy - UVOffset.xy;
    //vec4    color= texture( ColorMap, texcoord.xy ); //** 削除
    vec4    color= vec4( 1.0, 1.0, 0.2, 1.0 );
    out_FragColor.xyz= color.xyz * diff;
    out_FragColor.w= 1.0;
}

↓VertexShader の方は変化していませんが、Fragment Shader の出力が
大きく減っていることがわかります。
texture()が無いので不要と判断され UVOffset も消えました。

// ●テクスチャ無し Pipeline Object

// GeForce 4.4 : Vertex Shader
 Prg: 0011   Del=0 Link=1 Vali=1 Separable=1
Resource [Uniform]
  0: FLOAT_MAT4  Array=0 Loc=0 V_____  "PView"
  1: FLOAT_VEC4  Array=0 Loc=1 V_____  "UVOffset"
  2: FLOAT_MAT4  Array=0 Loc=2 V_____  "World"
Resource [Input]
  0: FLOAT_VEC3  Array=0       V_____  "NORMAL"
  1: FLOAT_VEC3  Array=0       V_____  "POSITION"
  2: FLOAT_VEC2  Array=0       V_____  "TEXCOORD"
Resource [Output]
  0: FLOAT_VEC3  Array=0       V_____  "onormal"
  1: FLOAT_VEC2  Array=0       V_____  "otexcoord"
  2: FLOAT_VEC4  Array=0       V_____  "gl_Position"


// GeForce 4.4 : Fragment Shader
Resource [Input]
  0: FLOAT_VEC3  Array=0       ____F_  "onormal"
Resource [Output]
  0: FLOAT_VEC4  Array=0       ____F_  "out_FragColor"

↓RADEON はちょっとだけ違います。

// RADEON 4.3 : Fragment Shader
Resource [Input]
  0: FLOAT_VEC3  Array=0       ____F_  "onormal"
  1: FLOAT_VEC2  Array=0       ____F_  "otexcoord"
Resource [Output]
  0: FLOAT_VEC4  Array=0       ____F_  "out_FragColor"

↓同じシェーダーを Pipeline Object を使わずに作ってみます。
Separable (GL_PROGRAM_SEPARABLE) の値で区別できます。

// ●テクスチャあり Program Object

// GeForce 4.4 : Vertex + Fragment Shader
 Prg: 0014   Del=0 Link=1 Vali=1 Separable=0
  [0] 0012 VERTEX_SHADER           Del=1 Compile=1
  [1] 0013 FRAGMENT_SHADER         Del=1 Compile=1
Resource [Uniform]
  0: SAMPLER_2D  Array=0 Loc=0 ____F_  "ColorMap"
  1: FLOAT_MAT4  Array=0 Loc=1 V_____  "PView"
  2: FLOAT_VEC4  Array=0 Loc=2 V___F_  "UVOffset"
  3: FLOAT_MAT4  Array=0 Loc=3 V_____  "World"
Resource [Input]
  0: FLOAT_VEC3  Array=0       V_____  "NORMAL"
  1: FLOAT_VEC3  Array=0       V_____  "POSITION"
  2: FLOAT_VEC2  Array=0       V_____  "TEXCOORD"
Resource [Output]
  0: FLOAT_VEC4  Array=0       ____F_  "out_FragColor"

texture() を削除したものが下記の通り。

// ●テクスチャ無し Program Object

// GeForce 4.4 : Vertex + Fragment Shader
 Prg: 0014   Del=0 Link=1 Vali=1 Separable=0
  [0] 0012 VERTEX_SHADER           Del=1 Compile=1
  [1] 0013 FRAGMENT_SHADER         Del=1 Compile=1
Resource [Uniform]
  0: FLOAT_MAT4  Array=0 Loc=0 V_____  "PView"
  1: FLOAT_MAT4  Array=0 Loc=1 V_____  "World"
Resource [Input]
  0: FLOAT_VEC3  Array=0       V_____  "NORMAL"
  1: FLOAT_VEC3  Array=0       V_____  "POSITION"
Resource [Output]
  0: FLOAT_VEC4  Array=0       ____F_  "out_FragColor"

↑Fragment Shader の最適化が Vertex Shader まで派生していることがわかります。
Fragment Vertex 双方から不要と判断され UVOffset が消えました。
GeForce に至っては Attribute の TEXCOORD まで無くなっています。

// RADEON 4.3 : Vertex + Fragment Shader
 Prg: 0014   Del=0 Link=1 Vali=1 Separable=0
  [0] 0010 VERTEX_SHADER           Del=1 Compile=1
  [1] 0013 FRAGMENT_SHADER         Del=1 Compile=1
Resource [Uniform]
  0: FLOAT_MAT4  Array=0 Loc=0 V_____  "PView"
  1: FLOAT_MAT4  Array=0 Loc=1 V_____  "World"
Resource [Input]
  0: FLOAT_VEC3  Array=0       V_____  "NORMAL"
  1: FLOAT_VEC3  Array=0       V_____  "POSITION"
  2: FLOAT_VEC2  Array=0       V_____  "TEXCOORD"
Resource [Output]
  0: FLOAT_VEC4  Array=0       ____F_  "out_FragColor"

このように、今までどおり Link して単一の Program Object に変換する方が
最適化の面ではメリットがあることがわかります。

実パフォーマンスでどの程度影響が生じるかどうかは未確認です。
Direct3D で開発し、すでにシェーダー管理の仕組みができている場合は
互換性の意味でも便利な機能です。
自由な組み合わせが必要な場合とそうでない場合の使い分けが必要かもしれません。

2014/06/23訂正: glPipelineObjects を glProgramPipelines に修正しました。間違った記載をしており申し訳ありませんでした。
小川さんご指摘ありがとうございます。

関連エントリ
OpenGL 4.2/4.3 Shader Resource と Buffer API
OpenGL ES 3.0/OpenGL 4.x Uniform Block
OpenGL の各バージョンと GLSL の互換性
OpenGL のエラー判定と OpenGL 4.3 Debug Output
OpenGL ES 3.0/OpenGL 4.4 Texture Object と Sampler Object
OpenGL ES 3.0 と Vertex Array Object

OpenGL 4.2/4.3 Shader Resource と Buffer API

OpenGL 3.1 以降や OpenGL ES 3.0 ではシェーダーが Uniform Buffer に
アクセスすることが出来ます。
OpenGL 4.2 以降はさらにシェーダーが利用できる Buffer Type が増えています。

OpenGL の API は基本的に下記の構造になっています。

●(1) Object を生成したり情報にアクセスするための Bind Point
  ・(A) (1) に Bind するための命令
  ・(B) (1) の Bind Point を使った設定や情報アクセス API

●(2) シェーダーに渡すための Bind Point の配列 (Table)
  ・(C) (2) に Bind するための専用命令
  ・(D) シェーダーのシンボルが (2) の Table のどこを見るか関連付ける命令

Texture Object は (A) と (C) の Bind 命令が共通なので、
glActiveTexture() で Bind Point ごと切り替えています。
切り替えるだけなので (1) と (2) のエリアが分かれていません。(詳細)

 (A) = glBindTexture()
 (C) = glActiveTexture() + glBindTexture()
 (D) = glUseProgram() + glUniform1i() ( 4.2 以降 layout(binding=n) )

Vertex Buffer (GL_ARRAY_BUFFER) は glVertexAttribPointer() 命令が
内部で (C) を暗黙のうちに行います。
その後 OpenGL 4.3 で明示的に割り付ける命令が追加されました。
(C) の命令は (1) に影響を与えません。(詳細)

 (A) = glBindBuffer( GL_ARRAY_BUFFER )
 (C) = glBindVertexBuffer()
 (D) = glVertexAttribBinding()

Uniform Buffer は 前回 のとおりです。
(1) と (2) は別の領域ですが、(C) の命令は (1)/(2) の両方に設定を行います。

 (A) = glBindBuffer( GL_UNIFORM_BUFFER )
 (C) = glBindBufferBase( GL_UNIFORM_BUFFER )
 (D) = glUniformBlockBinding() ( 4.2 以降 layout(binding=n) )

OpenGL 4.2 以降は Bind Point を Shader 内に記述できるので、
いくつかのケースでは (D) を用いなくても良くなっています。
OpenGL 4.2 で追加された Atomic Counter Buffer も Shader 側で指定します。

● Atomic Counter Buffer (4.2)

Atomic Counter Buffer (GL_ATOMIC_COUNTER_BUFFER) は Uniform として宣言
しますが、シェーダーが値を更新することができます。

// Init
struct CounterData {
  unsigned int Counter1;
  unsigned int Counter2;
};
CounterData  counter;
memset( &counter, 0, sizeof(CounterData) );
GLuint CounterBlock= 0;
glGenBuffers( 1, &CounterBlock );
glBindBuffer( GL_ATOMIC_COUNTER_BUFFER, CounterBlock );
glBufferData( GL_ATOMIC_COUNTER_BUFFER, sizeof(CounterData), &counter, GL_DYNAMIC_DRAW );
// Render
// 初期化
CounterData counter;
memset( &counter, 0, sizeof(CounterData) );
glBindBuffer( GL_ATOMIC_COUNTER_BUFFER, CounterBlock );
glBufferSubData( GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(CounterData), &counter );
glBindBuffer( GL_ATOMIC_COUNTER_BUFFER, 0 );

~
glBindBufferBase( GL_ATOMIC_COUNTER_BUFFER, 3, CounterBlock ); // Bind
glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0 );

API の構造は Uniform Block と同じです。
atomic_uint は特殊な型で、アクセスには専用の命令が必要となります。

// GLSL : vsh
uniform mat4  PVW;
uniform mat4  World;

layout(binding=3, offset=0) uniform atomic_uint  Counter1;

in vec3 POSITION;
in vec3 NORMAL;

out vec3 onormal;

void main()
{
    vec4  opos= vec4( POSITION.xyz, 1.0 ) * PVW;
    opos.z= 2.0 * opos.z - opos.w;
    gl_Position= opos;

    atomicCounterIncrement( Counter1 );

    onormal= NORMAL.xyz * mat3x3( World );
}

Vertex / Fragment それぞれ実行回数を数えているだけです。

// GLSL : fsh
in vec3  onormal;

layout(binding=3, offset=4) uniform atomic_uint	Counter2;

void main()
{
    uint   param= atomicCounterIncrement( Counter2 ) & 0xffff;
    float  color= clamp( float( param ) * 0.001, 0.0, 1.0 );
    float  diff= clamp( dot( vec3( 0.0, 0.0, 1.0 ), normalize( onormal.xyz ) ), 0.0, 1.0 );
    out_FragColor.xyz= vec3( color, 1.0, color ) * diff;
    out_FragColor.w= 1.0;
}

RADEON (13.8beta) では Beta 版 Drive のせいか若干挙動に問題がありました。
vsh/fsh のカウンタが加算された値が返ってきます。
また API の Bind Point の構造が Uniform Block と異なっているようで
GL_ATOMIC_COUNTER_BUFFER_BINDING を glGetIntegeri_v() で取るとエラーでした。

GL_SHADER_STORAGE_BUFFER_BINDING の方も、
(1) の Bind が (2) の 0番と共有されているか、もしくは
glGetIntegerv( GL_SHADER_STORAGE_BUFFER_BINDING ) が (2) の
0 番の内容を返しているように見えます。

● Shader Storage Buffer (4.3)

OpenGL 4.3 の GLSL 4.3 では、Uniform Block 同様の構文で buffer 宣言が
追加されています。
当初 Direct3D の HLSL でいう cbuffer に対する tbuffer のことだと
思っていたのですが、直接データを書き換えることもできるようです。
つまり Direct3D の UnorderedAccessView (UAV) に相当する役目も持っています。

// GLSL
layout(std140, binding=3) buffer MatBlock {
    layout(column_major) readonly mat4	MatData1;
    layout(column_major) readonly mat4	MatData2;
};

こちらも簡単な動作を確認したのですが、GeForce では意図したとおりに
動くものの RADEON HD7750 の 13.8beta ではまだ少々不安定でした。
(使い方に問題があった可能性もあります)

● GL_ARB_program_interface_query

Uniform Block には Bind したり Program から情報を得るための専用の API がありました。
OpenGL 4.3 では Buffer Type も増えていることから、
専用の API を増やす代わりにより一般的な API が用意されています。

新しい Shader Storage Buffer だけでなく、Atomic Counter Buffer や
従来の Uniform Block, Uniform, Attribute などもこの API に統合されています。
Uniform Block も下記の通り。

glGetProgramiv( program, GL_ACTIVE_UNIFORM_BLOCKS )
↓
glGetProgramInterfaceiv( program, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES )

glGetActiveUniformBlockiv( program, index, ~ );
↓
glGetProgramResourceiv( program, GL_UNIFORM_BLOCK, index, ~ );

下記は実際にいろいろ情報を取り出してみた結果です。
少々わかりにくいですが、Atomic Counter Buffer や Shader Storage Buffer
だけでなく、同じ API で Attribute や出力変数も取り出すことが出来ます。

// RADEON
Resource [Uniform] 92e1: 5 (Prog=0012)
  0: Loc=0000 FLOAT_MAT4  (8b5c) Array=0 BL=-1 off=-1 ast=-1 mst=-1 RM=0 AC=-1 V_____ "World"
  1: Loc=ffff FLOAT_MAT4  (8b5c) Array=0 BL= 0 off= 0 ast= 0 mst=16 RM=0 AC=-1 V_____ "Projection"
  2: Loc=ffff FLOAT_MAT4  (8b5c) Array=0 BL= 0 off=64 ast= 0 mst=16 RM=0 AC=-1 V_____ "ViewMatrix"
  3: Loc=ffff UNSIGNED_INT_ATOMIC_COUNTER(92db) Array=0 BL=-1 off=-1 ast=-1 mst=-1 RM=0 AC= 0 V_____ "Counter"
  4: Loc=ffff UNSIGNED_INT_ATOMIC_COUNTER(92db) Array=0 BL=-1 off=-1 ast=-1 mst=-1 RM=0 AC= 1 ____F_ "Counter2"
Resource [Atomic] 92c0: 2 (Prog=0012)
  0: Bind=0000 Size=    4 Uni=1 Ind=2 V_____ ""
  1: Bind=0001 Size=    4 Uni=1 Ind=3 ____F_ ""
Resource [UniformBlock] 92e2: 1 (Prog=0012)
  0: Bind=0000 Size=  128 Uni=2 Ind=1 V_____ "Scene"
Resource [ShaderStorage] 92e6: 1 (Prog=0012)
  0: Bind=0003 Size=  128 Uni=2 Ind=0 ____F_ "MatBlock"
Resource [Input] 92e3: 3 (Prog=0012)
  0: Loc=ffff FLOAT_VEC3  (8b51) Array=0 BL=899973093 off=53214 ast= 3 mst=128 RM=2 AC= 0 V_____ "NORMAL"
  1: Loc=ffff FLOAT_VEC3  (8b51) Array=0 BL=899973093 off=53214 ast= 3 mst=128 RM=2 AC= 0 V_____ "POSITION"
  2: Loc=ffff FLOAT_VEC2  (8b50) Array=0 BL=899973093 off=53214 ast= 3 mst=128 RM=2 AC= 0 V_____ "TEXCOORD"
Resource [Output] 92e4: 1 (Prog=0012)
  0: Loc=ffff FLOAT_VEC4  (8b52) Array=0 BL=899973093 off=53214 ast= 3 mst=128 RM=2 AC= 0 ____F_ "out_FragColor"
Resource [Variable] 92e5: 2 (Prog=0012)
  0: Loc=ffff FLOAT_MAT4  (8b5c) Array=0 BL= 0 off= 0 ast= 0 mst=16 RM=0 AC= 1 ____F_ "Projection2"
  1: Loc=ffff FLOAT_MAT4  (8b5c) Array=0 BL= 0 off=64 ast= 0 mst=16 RM=0 AC= 1 ____F_ "ViewMatrix2"
// GeForce
Resource [Uniform] 92e1: 5 (Prog=0012)
  0: Loc=ffff UNSIGNED_INT_ATOMIC_COUNTER(92db) Array=0 BL= 0 off= 0 ast= 0 mst= 0 RM=0 AC= 0 V_____ "Counter"
  1: Loc=ffff UNSIGNED_INT_ATOMIC_COUNTER(92db) Array=0 BL= 0 off= 4 ast= 0 mst= 0 RM=0 AC= 0 ____F_ "Counter2"
  2: Loc=ffff FLOAT_MAT4  (8b5c) Array=0 BL= 0 off= 0 ast= 0 mst=16 RM=0 AC=-1 V_____ "Projection"
  3: Loc=ffff FLOAT_MAT4  (8b5c) Array=0 BL= 0 off=64 ast= 0 mst=16 RM=0 AC=-1 V_____ "ViewMatrix"
  4: Loc=0004 FLOAT_MAT4  (8b5c) Array=0 BL=-1 off=-1 ast=-1 mst=-1 RM=0 AC=-1 V_____ "World"
Resource [Atomic] 92c0: 1 (Prog=0012)
  0: Bind=0000 Size=    8 Uni=2 Ind=0 V___F_ ""
Resource [UniformBlock] 92e2: 1 (Prog=0012)
  0: Bind=0004 Size=  128 Uni=2 Ind=2 V_____ "Scene"
Resource [ShaderStorage] 92e6: 1 (Prog=0012)
  0: Bind=0003 Size=  128 Uni=1 Ind=0 V_____ "MatBlock"
Resource [Input] 92e3: 2 (Prog=0012)
  0: Loc=ffff FLOAT_VEC3  (8b51) Array=0 BL=-212702986 off=38875 ast= 3 mst=128 RM=1 AC= 0 V_____ "NORMAL"
  1: Loc=ffff FLOAT_VEC3  (8b51) Array=0 BL=-212702986 off=38875 ast= 3 mst=128 RM=1 AC= 0 V_____ "POSITION"
Resource [Output] 92e4: 1 (Prog=0012)
  0: Loc=ffff FLOAT_VEC4  (8b52) Array=0 BL=-212702986 off=38875 ast= 3 mst=128 RM=1 AC= 0 ____F_ "out_FragColor"
Resource [Variable] 92e5: 1 (Prog=0012)
  0: Loc=ffff FLOAT_MAT4  (8b5c) Array=0 BL= 0 off=64 ast= 0 mst=16 RM=0 AC=-1 V_____ "ViewMatrix2"

関連エントリ
OpenGL ES 3.0/OpenGL 4.x Uniform Block
OpenGL の各バージョンと GLSL の互換性
OpenGL のエラー判定と OpenGL 4.3 Debug Output
OpenGL ES 3.0/OpenGL 4.4 Texture Object と Sampler Object
OpenGL ES 3.0 と Vertex Array Object

OpenGL ES 3.0/OpenGL 4.x Uniform Block

Uniform Block は Direct3D の Constant Buffer に相当します。
外部で確保したメモリを、Shader からアクセス可能な Uniform 変数として
割り付けられるようになります。

トータルで Uniform として使えるメモリが増えますし、Shader 間での
データ共有も可能です。

OpenGL の Shader Program は D3D の FX に近い機能を持っていました。
シンボルは Shader 同士リンクされた状態となり、uniform の割り当てや
管理も自動的に行なってくれるため大変扱いやすくなっています。

その反面 GL 3.0 以前は uniform 値も Program Object 内で閉じており、
Camera や Light 等のシーンで共通なパラメータも、シェーダーそれぞれに
コピーする必要がありました。

OpenGL 3.1 と OpenGL ES 3.0 以降は予め Uniform Buffer を作って
おくことができます。
描画時は各シェーダーにバッファを bind するだけです。
その代わり、バッファ内の変数配置を自分で管理する必要があります。

// GLSL vsh
layout(std140) uniform Scene {
  mat4  Projection;
  mat4  ViewMatrix;
};

uniform mat4   World;
in vec3 POSITION;

void main()
{
   mat4 pvw= World * ViewMatrix * Projection;
   gl_Position.xyzw= vec4( POSITION.xyz, 1.0 ) * pvw;
}

↑シェーダー内では構造体のような書式で uniform block を宣言します。
HLSL の cbuffer と全く同じ仕様で、インスタンス名を宣言しなければ
シンボル名はトップレベルのスコープに含まれます。
main() の中でそのまま ViewMatrix や Projection を参照しています。

// C Init
struct SceneData {
  math::Matrix4 Projection;
  math::Matrix4 ViewMatrix;
};
GLuint scene_uniform= 0;
glGenBuffers( 1, &scene_uniform );
glBindBuffer( GL_UNIFORM_BUFFER, scene_uniform );
glBufferData( GL_UNIFORM_BUFFER, sizeof(SceneData), &scene_data, GL_STATIC_DRAW );
glBindBuffer( GL_UNIFORM_BUFFER, 0 );

GLuint scene_block_index= glGetUniformBlockIndex( program, "Scene" );
glUniformBlockBinding( program, scene_block_index, 5 );

↑Uniform Block の作成は通常の Buffer と同じです。
操作対象を glBindBuffer() を使って bind します。
管理しやすいように GLSL 側の block と同じ構造体を宣言しています。

// C Render
glUseProgram( program );
glBindVertexArray( input_layout );

// shader local
glUniformMatrix4fv( world_location, 1, GL_FALSE, matrix );

// global
glBindBufferBase( GL_UNIFORM_BUFFER, 5, scene_uniform );

glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0 );

↑描画時の bind 命令は特殊です。

Uniform Block の API は、Texture Object や Sampler Object の
仕組みに似ています。

API 内部に Bind Point の Table が存在しており、Object はこの Table に登録します。
この場合の番号は任意で、利用可能な範囲内 (GL_MAX_UNIFORM_BUFFER_BINDINGS)
なら好きなものを選んで構いません。

glBindBufferBase( GL_UNIFORM_BUFFER, 5, scene_uniform );

シェーダーにはこの Table の番号を登録します。

glUniformBlockBinding( program, scene_block_index, 5 );

Texture は glUniform1i() を使いましたが Uniform Block は専用の
命令 glUniformBlockBinding() があります。

Texture の場合と命令を比較すると下記の通り

● Texture の場合

// shader 側の Unit 指定
glUseProgram( program );
GLint loc= glGetUniformLocation( "SamplerName" );
glUniform1i( loc, 5 );
//  glGetUniformLocation() は 存在しない場合 -1 を返す

// object のバインド
glActiveTexture( GL_TEXTURE0 + 5 );
glBindTexture( GL_TEXTURE_2D, texture_object );
● Uniform Block の場合

// shader 側の Unit 指定
GLuint bindex= glGetUniformBlockIndex( program, "BlockName" );
glUniformBlockBinding( program, bindex, 5 );
//   glGetUniformBlockIndex() は 存在しない場合 GL_INVALID_INDEX を返す

// object のバインド
glBindBufferBase( GL_UNIFORM_BUFFER, 5, scene_uniform );

Texture Object では、Object 操作用の Bind と
描画用の Texture Image Unit 指定の Bind が同一でした。
複数の Unit を切り替えるためには別命令 glActiveTexture() を併用します。

Uniform Block の場合は、Object 操作用の Bind と描画用の BInd Point が
分かれています。

Object 操作は Vertex Buffer や Index Buffer などと同じ glBindBuffer() です。
描画用の Bind 登録には glBindBufferBase() か glBindBufferRange() を使います。
これらの命令は Uniform 以外の Buffer でも用います。

glBindBufferBase() や glBindBufferRange() を実行すると、描画用の
Bind Point だけでなく操作用の Bind Point の両方が置き換わるので注意が必要です。

glBindBuffer( GL_UNIFORM_BUFFER, 0 );

DumpUniformBinding();
//  Buf: Uni=0000            通常の glBindBuffer() の値 (0クリア)

glBindBufferBase( GL_UNIFORM_BUFFER, 0, scene_uniform );
glBindBufferBase( GL_UNIFORM_BUFFER, 4, scene_uniform );

DumpUniformBinding();
//  Buf: Uni=000e            通常の glBindBuffer() の値 (ここも設定される)
// [ 0]: Bind=000e (0 - 0)   glBindBufferBase( 0 ) の値
// [ 4]: Bind=000e (0 - 0)   glBindBufferBase( 4 ) の値

glBindBuffer( GL_UNIFORM_BUFFER, 0 );

DumpUniformBinding();
//  Buf: Uni=0000            通常の glBindBuffer() の値 (ここだけ 0)
// [ 0]: Bind=000e (0 - 0)   glBindBufferBase( 0 ) の値
// [ 4]: Bind=000e (0 - 0)   glBindBufferBase( 4 ) の値

上記のように glBindBufferBase() は通常の glBindBuffer() の bind point も書き換えます。
ですが glBindBuffer() を実行しても、描画用の Bind Point には影響が出ません。

よって下記のように glDrawElements() の前に
glBindBuffer( GL_UNIFORM_BUFFER, 0 ) を入れても問題ありません。

glUseProgram( program );
glBindVertexArray( input_layout );
glUniformMatrix4fv( world_location, 1, GL_FALSE, matrix );
glBindBufferBase( GL_UNIFORM_BUFFER, 5, scene_uniform );

glBindBuffer( GL_UNIFORM_BUFFER, 0 ); // 問題なし

glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0 );

なお glBindBufferBase() でバッファを登録しても GL_UNIFORM_BUFFER_SIZE
は 0 のままでした。RADEON だけ値が入っています。

GPU                    GL_UNIFORM_BUFFER_SIZE
---------------------------------------------
Intel HD 4000      4.0          0
GeForce GTX 650    4.4          0
RADEON HD 6750M    4.3        128
Mali-T604   OpenGL ES 3.0       0

下記は Uniform Block の利用可能なサイズを調べた結果です。

                        Intel  GeForce  RADEON    RADEON
             Mali-T604  HD4000 GTX650M  HD6750M   HD7750(GCN)
------------------------------------------------------------
Vertex:
 Components     1024     4096     4096    16384     1024
 Blocks           12       12       14       15       15
 Combined      50176    53248   233472   262144   246784

Fragment:
 Components     4096     4096     2048    16384     1024
 Blocks           12       12       14       15       15
 Combined      53248    53248   231424   262144   246784

Hull (tcsh):
 Components              4096     2048    16384    16384
 Blocks                    12       14       15       15
 Combined               53248   231424   262144   246784

Domain (tesh):
 Components              4096     2048    16384    16384
 Blocks                    12       14       15       15
 Combined               53248   231424   262144   246784

Geometry:
 Components              4096     2048    16384    16384
 Blocks                    12       14       15       15
 Combined               53248   231424   262144   246784

Compute:
 Components                       2048     1024     1024
 Blocks                             14       16       16
 Combined                       231424    32768     1024

Bindings          36       60       84       75       75
BlockSize      16384    16384    65536    65536    65536
CombindBlocks     24       60       84       75       75

Direct3D API では shader 毎に 4096 elements の block を 14 slot まで
利用できます。(OpenGL の components と blocks に相当)
GL API では上記のように GPU 毎に異なる値となっています。

関連エントリ
OpenGL の各バージョンと GLSL の互換性
OpenGL のエラー判定と OpenGL 4.3 Debug Output
OpenGL ES 3.0/OpenGL 4.4 Texture Object と Sampler Object
OpenGL ES 3.0 と Vertex Array Object

OpenGL の各バージョンと GLSL の互換性

OpenGL のシェーディング言語 GLSL は、OpenGL の API と同様に
活発な更新が行われています。
Direct3D の HLSL よりも種類が多く、バージョン間には機能差があります。

GPU に関する機能だけでなく、言語の構文なども拡張が続けられているようです。
いくつかの機能に関して GLSL バージョン間の違いをまとめてみました。

GLSL Version

下記の表は部分的にピックアップしています。

// GL2 Group
OpenGL        GLSL         version 行      suffix   ITC
--------------------------------------------------------
OpenGL ES 2.0 GLSL ES 1.0  #version 100    -        -
OpenGL 2.0    GLSL 1.1                     -        -
OpenGL 2.1    GLSL 1.2     #version 120    f        ◎
// GL3/4 Group
OpenGL        GLSL        version 行       suffix  ITC  UBlock
----------------------------------------------------------------
OpenGL 3.0    GLSL 1.3	  #version 130     u,f      ◎  -
OpenGL 3.1    GLSL 1.4	  #version 140     u,f      ◎  U
OpenGL 3.2    GLSL 1.5	  #version 150     u,f      ◎  U,I,O
OpenGL ES 3.0 GLSL ES 3.0 #version 300 es  u,f      -   U
OpenGL 3.3    GLSL 3.3	  #version 330     u,f      ◎  U,I,O
OpenGL 4.0    GLSL 4.0	  #version 400     u,f,lf   ◎  U,I,O
OpenGL 4.1    GLSL 4.1	  #version 410     u,f,lf   ◎  U,I,O
OpenGL 4.2    GLSL 4.2	  #version 420     u,f,lf   ◎  U,I,O
OpenGL 4.3    GLSL 4.3	  #version 430     u,f,lf   ◎  U,I,O,B
OpenGL 4.4    GLSL 4.4	  #version 440     u,f,lf   ◎  U,I,O,B

GLSL は上位互換性を保っており、コンパイラは
1行目の「#version」行でバージョンを判定しています。

まず OpenGL 2.x 系と OpenGL 3.0 以降で大きな違いがあります。
attribute/varying と in/out のキーワードが変更され、
texture2D() 等の関数名も変わりました。

uint/uvec (unsigned int) や switch 文が使えるようになったのも
OpenGL 3.0 (GLSL 1.3) からです。

double 型の対応は OpenGL 4.0 (GLSL 4.0) 以降。
Uniform Block (Constant Buffer) は OpenGL 3.1 (GLSL 1.4) で追加されましたが、
in/out の Interface Block は 3.2 (GLSL 1.5) で対応しました。

GLSL は C言語や他のシェーダー言語と違い、初期化にはコンストラクタが必要でした。

// GLSL
vec3  pos= vec3( 1.0, 2.0, 3.0 );
float a[4]= float[]( 1.0, 2.0, 3.0, 4.0 );

OpenGL 4.2 (GLSL 4.2) 以降は C言語ライクな初期化リスト {~} が使えるように
なっており下記の書式が通ります。

// GLSL
vec3  pos= { 1.0, 2.0, 3.0 };
float a[4]= { 1.0, 2.0, 3.0. 4.0 };

予想に反して OpenGL 4.0 の Intel HD 4000 (9.18.10.3165) でもコンパイルが通りました。

OpenGL ES 3.0 はいくつか仕様が削られているようです。
特に暗黙の型変換ができません。↓がエラー。

float a= 1;   //  int to float

OpenGL ES 2.0 でも自動変換出来なかったので、この場合は 1.0 と記述する
必要がありました。
(OpenGL ES 2.0 だと suffix も使えないので 1.0f と書いてもエラー)

OpenGL ES 3.0 は符号なし整数型でも同様の問題が起こります。

uint  i= 32;   //  int to uint

下記のように ‘u’ が必要です。

uint  i= 32u;

暗黙の型変換があるとオーバーロード関数の検索も一気に複雑になるので、
組み込み用途では不要だと判断したのかもしれません。
ただしコンパイラがきちんとエラーを返してくれるので、
これらの問題はすぐに判明します。

●互換性

HD 4000 で Initializer List が使えたので他の GPU でも実験してみました。

GPU              OpenGL      GLSL version    compile
----------------------------------------------------
Intel HD 4000    OpenGL 4.0  #version 400      OK
RADEON HD 6750M  OpenGL 4.3  #version 430      OK
RADEON HD 6750M  OpenGL 4.3  #version 400      OK
GeForce GTX 650  OpenGL 4.4  #version 440      OK
GeForce GTX 650  OpenGL 4.4  #version 400     Error

GLSL の version を下げると GeForce ではエラーになりますが
RADEON では通りました。
GLSL の仕様上はエラーの方が正解です。
A では動くが他社の B では動かないといった違いは、意外にこの辺りが
原因なのかもしれません。

OpenGL のエラー判定と OpenGL 4.3 Debug Output

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 parameter  has 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 のはじめ方