日別アーカイブ: 2013年8月16日

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