はじめに
OpenGL のシェーダにデータを受け渡す方法の一つにユニフォームがありますが、 ユニフォームを少し拡張したような機能でユニフォームブロックというものもあってこれについて調べたことについて書きます。
仕様
ユニフォームブロックにはユニフォームバッファオブジェクトを使うことでまとめてユニフォームを受け渡すことができます。
ライトやカメラの情報など複数のシェーダで使う情報を受け渡すときに便利なのではないかと思います。
しかしバッファにユニフォームのデータを書き込むときにはユニフォームブロックのメモリレイアウトについて考慮する必要があって、この部分が少し難しく間違いやすい気がします。
メモリレイアウトの種類として shared, packed, std140 があります。デフォルトは shared のようです。std140 を使うことが多いと思うのでこれのみ調べています。
OpenGL 仕様の 7.6.2.2 Standard Uniform Block Layout のあたりにかかれていました。
https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf
std140 のメモリレイアウト仕様の引用
- If the member is a scalar consuming N basic machine units, the base alignment is N.
- If the member is a two- or four-component vector with components consuming N basic machine units, the base alignment is 2N or 4N, respectively.
- If the member is a three-component vector with components consuming N basic machine units, the base alignment is 4N.
- If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array element, according to rules (1), (2), and (3), and rounded up to the base alignment of a vec4.
- If the member is a column-major matrix with C columns and R rows, the matrix is stored identically to an array of C column vectors with R components each, according to rule (4).
- If the member is an array of S column-major matrices with C columns and R rows, the matrix is stored identically to a row of S × C column vectors with R components each, according to rule (4).
- If the member is a row-major matrix with C columns and R rows, the matrix is stored identically to an array of R row vectors with C components each, according to rule (4).
- If the member is an array of S row-major matrices with C columns and R rows, the matrix is stored identically to a row of S × R row vectors with C components each, according to rule (4).
- If the member is a structure, the base alignment of the structure is N, where N is the largest base alignment value of any of its members, and rounded up to the base alignment of a vec4.
The individual members of this substructure are then assigned offsets by applying this set of rules recursively, where the base offset of the first member of the sub-structure is equal to the aligned offset of the structure.
The structure may have padding at the end; the base offset of the member following the sub-structure is rounded up to the next multiple of the base alignment of the structure. - If the member is an array of S structures, the S elements of the array are laid out in order, according to rule (9).
訳してみたもの
- スカラーでNマシンユニットを消費する場合アラインメントはN。
- 2または4要素のベクトルで要素がNマシンユニットを消費する場合、アラインメントはそれぞれ2N、4N。
- 3要素ベクトルで要素がNマシンユニットを消費するときのアラインメントは4N。
- 配列のアラインメントは要素から計算される。ただしvec4で切り上げられる。
- 列優先C列R行行列はルール4からR要素ベクトルのサイズCの配列と同じ。
- 列優先C列R行行列のS要素配列の場合、R要素ベクトルを要素とするサイズS x Cの配列と同じ。
- 行優先C列R行行列の場合、C要素ベクトルのサイズRの配列と同じ。
- 行優先C列R行の行列S要素配列の場合、C要素ベクトルを要素とするサイズS x Rの配列と同じ。
- 構造体の場合、アラインメントは構造体のメンバーのうち一番大きいもののアラインメントになる。ただしvec4アラインメントに切り上げられる。
構造体の個々のメンバーはこのルールを再帰的に適用され決まったオフセットの位置に配置される。構造体の最初のメンバーの配置オフセットは構造体自身のオフセットに一致する。
構造体は末尾にパディングを持つ場合がある。構造体に続くメンバーのベースオフセットは、構造体のベースアライメントの次の倍数に切り上げられる。 - 構造体を要素とするサイズSの配列の場合、ルール9によって順番に配置される。
スカラー型は bool, float, int, uint, double があります。
スカラー型のアラインメントとバイトサイズは下のようになっています。
| uniform type | alignment | byte size |
|---|---|---|
| bool | 4 | 4 |
| float | 4 | 4 |
| int | 4 | 4 |
| uint | 4 | 4 |
| double | 8 | 8 |
それから行列のメモリレイアウトの列優先 (column-major)、行優先 (row-major) は glsl 側で指定できて column_major または row_major で指定します。
デフォルトは列優先です。
https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Matrix_storage_order
調べ方
仕様を読んだり google で検索したりしてたんですがはっきりした情報が見つからずコードを書いて実際にどうなるかみてみることにしました。
https://github.com/tkaaad97/uniformblock-experiment
使ったコードはここのリポジトリに置いています。
適当なユニフォームブロックが入ったシェーダーを書いてこれを OpenGL でコンパイルし、ユニフォームの情報を取得して調べるという方法で試しました。
下のような感じでブロック一つ目のフィールドをアラインメントが一番小さい float にして二つ目のフィールドを調べたい型にし、三つ目を float にしてオフセットを取得することでアラインメントとサイズが多分わかると思います。
layout (std140) uniform ublock {
float uniform1;
#{uniformType} uniform2;
float uniform3;
} ublock;
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetActiveUniformsiv.xhtml
ユニフォームのパラメーターは glGetActiveUniformsiv で取得できます。
パラメーターの種類は GL_UNIFORM_SIZE, GL_UNIFORM_NAME_LENGTH, GL_UNIFORM_BLOCK_INDEX, GL_UNIFORM_OFFSET, GL_UNIFORM_ARRAY_STRIDE, GL_UNIFORM_MATRIX_STRIDE, GL_UNIFORM_IS_ROW_MAJOR GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX です。
また構造体についてはパターンが色々あって網羅できないためこの方法では調べていません。 他の部分が分かれば構造体については仕様のルールから理解できるはずと思います。
結果
下のような結果になりました。 仕様の文章からわかる結果だと思いますが、確証が持てなかったのではっきりしてよかったです。
| uniform type | alignment | byte size | array stride | matrix stride | matrix order |
|---|---|---|---|---|---|
| bvec2 | 8 | 8 | |||
| bvec3 | 16 | 12 | |||
| bvec4 | 16 | 16 | |||
| vec2 | 8 | 8 | |||
| vec3 | 16 | 12 | |||
| vec4 | 16 | 16 | |||
| ivec2 | 8 | 8 | |||
| ivec3 | 16 | 12 | |||
| ivec4 | 16 | 16 | |||
| uvec2 | 8 | 8 | |||
| uvec3 | 16 | 12 | |||
| uvec4 | 16 | 16 | |||
| dvec2 | 16 | 16 | |||
| dvec3 | 32 | 24 | |||
| dvec4 | 32 | 32 | |||
| float[4] | 16 | 64 | 16 | ||
| vec2[4] | 16 | 64 | 16 | ||
| vec3[4] | 16 | 64 | 16 | ||
| vec4[4] | 16 | 64 | 16 | ||
| dvec2[4] | 16 | 64 | 16 | ||
| dvec3[4] | 32 | 128 | 32 | ||
| dvec4[4] | 32 | 128 | 32 | ||
| mat2 | 16 | 32 | 16 | row | |
| mat2x3 | 16 | 48 | 16 | row | |
| mat2x4 | 16 | 64 | 16 | row | |
| mat3 | 16 | 48 | 16 | row | |
| mat3x2 | 16 | 32 | 16 | row | |
| mat3x4 | 16 | 64 | 16 | row | |
| mat4 | 16 | 64 | 16 | row | |
| mat4x2 | 16 | 32 | 16 | row | |
| mat4x3 | 16 | 48 | 16 | row | |
| dmat2 | 16 | 32 | 16 | row | |
| dmat2x3 | 16 | 48 | 16 | row | |
| dmat2x4 | 16 | 64 | 16 | row | |
| dmat3 | 32 | 96 | 32 | row | |
| dmat3x2 | 32 | 64 | 32 | row | |
| dmat3x4 | 32 | 128 | 32 | row | |
| dmat4 | 32 | 128 | 32 | row | |
| dmat4x2 | 32 | 64 | 32 | row | |
| dmat4x3 | 32 | 96 | 32 | row | |
| mat2[4] | 16 | 128 | 32 | 16 | row |
| mat2x3[4] | 16 | 192 | 48 | 16 | row |
| mat2x4[4] | 16 | 256 | 64 | 16 | row |
| mat3[4] | 16 | 192 | 48 | 16 | row |
| mat3x2[4] | 16 | 128 | 32 | 16 | row |
| mat3x4[4] | 16 | 256 | 64 | 16 | row |
| mat4[4] | 16 | 256 | 64 | 16 | row |
| mat4x2[4] | 16 | 128 | 32 | 16 | row |
| mat4x3[4] | 16 | 192 | 48 | 16 | row |
| dmat2[4] | 16 | 128 | 32 | 16 | row |
| dmat2x3[4] | 16 | 192 | 48 | 16 | row |
| dmat2x4[4] | 16 | 256 | 64 | 16 | row |
| dmat3[4] | 32 | 384 | 96 | 32 | row |
| dmat3x2[4] | 32 | 256 | 64 | 32 | row |
| dmat3x4[4] | 32 | 512 | 128 | 32 | row |
| dmat4[4] | 32 | 512 | 128 | 32 | row |
| dmat4x2[4] | 32 | 256 | 64 | 32 | row |
| dmat4x3[4] | 32 | 384 | 96 | 32 | row |
| mat2 | 16 | 32 | 16 | column | |
| mat2x3 | 16 | 32 | 16 | column | |
| mat2x4 | 16 | 32 | 16 | column | |
| mat3 | 16 | 48 | 16 | column | |
| mat3x2 | 16 | 48 | 16 | column | |
| mat3x4 | 16 | 48 | 16 | column | |
| mat4 | 16 | 64 | 16 | column | |
| mat4x2 | 16 | 64 | 16 | column | |
| mat4x3 | 16 | 64 | 16 | column | |
| dmat2 | 16 | 32 | 16 | column | |
| dmat2x3 | 32 | 64 | 32 | column | |
| dmat2x4 | 32 | 64 | 32 | column | |
| dmat3 | 32 | 96 | 32 | column | |
| dmat3x2 | 16 | 48 | 16 | column | |
| dmat3x4 | 32 | 96 | 32 | column | |
| dmat4 | 32 | 128 | 32 | column | |
| dmat4x2 | 16 | 64 | 16 | column | |
| dmat4x3 | 32 | 128 | 32 | column | |
| mat2[4] | 16 | 128 | 32 | 16 | column |
| mat2x3[4] | 16 | 128 | 32 | 16 | column |
| mat2x4[4] | 16 | 128 | 32 | 16 | column |
| mat3[4] | 16 | 192 | 48 | 16 | column |
| mat3x2[4] | 16 | 192 | 48 | 16 | column |
| mat3x4[4] | 16 | 192 | 48 | 16 | column |
| mat4[4] | 16 | 256 | 64 | 16 | column |
| mat4x2[4] | 16 | 256 | 64 | 16 | column |
| mat4x3[4] | 16 | 256 | 64 | 16 | column |
| dmat2[4] | 16 | 128 | 32 | 16 | column |
| dmat2x3[4] | 32 | 256 | 64 | 32 | column |
| dmat2x4[4] | 32 | 256 | 64 | 32 | column |
| dmat3[4] | 32 | 384 | 96 | 32 | column |
| dmat3x2[4] | 16 | 192 | 48 | 16 | column |
| dmat3x4[4] | 32 | 384 | 96 | 32 | column |
| dmat4[4] | 32 | 512 | 128 | 32 | column |
| dmat4x2[4] | 16 | 256 | 64 | 16 | column |
| dmat4x3[4] | 32 | 512 | 128 | 32 | column |