GLSL一覧

GLSLのシェーダーリフレクション (2)

OpenGL (GLSL) のユニフォームブロックでのシェーダーリフレクションを試してみました。
ここで使った vertexShader, fragment shaderは以下のようなものです。

これらのシェーダーについて情報を出してみるとこのような感じになりました。
まずはシェーダーユニフォームの一覧.

ユニフォームブロックの中身のメンバには、ロケーションIDは振られていないことが確認できます。ユニフォームのインデックスは振られており、情報はこちら軽油で取得できるようです。
そして、ユニフォームブロックに関して情報を取得してみます。

ユニフォームブロック内でのシェーダー定数のオフセットが取得できました。
ユニフォームブロック内に構造体を配置しても、従来のシェーダー定数の場合と変わらないような名前でオフセットやデータ型を取得できることがわかります。

std140 とかレイアウト指定をしない場合、シェーダーによって(環境によって?)定数の位置が変わりそうです。
そのため、UBOを複数のシェーダーで使い回すのは難しそうです。この場合結局定数毎に書き込む位置を見てバッファ内のデータ更新という手続きをとるので、あまりUBOによる描画効率化に繋がらないように感じます。

ユニフォームブロック内の定数であっても GL_ACTIVE_UNIFORMS にて有効なユニフォーム変数としてカウントされていることに少々驚きました。ただしユニフォームブロック内の変数名からは有効なロケーションが取得できません。ユニフォームブロック内の変数についてはロケーション=-1となっているようです。


GLSLのシェーダーリフレクション

GLSLいじりの過程で、シェーダーリフレクションっぽいことをやってみたのでその調査結果です。

OpenGLでは別に他のライブラリに依存することなく、シェーダーリフレクションの機能を持っています。普段ではuniform変数のロケーション情報を名前から取得しているかと思いますが、その延長上でいろいろな情報をとれるようになっています。

そこで次のようなシェーダープログラム(抜粋)で、どのように情報がとれてくるか試してみました。

結果はこんな感じでした。ドライバの状態によっては別の結果を返してきそうな気配も・・・。AMDやIntelで試してみたいところです。

NVIDIA 331.65 のドライバの結果。

ここからわかることは、次のような感じかと。

  • 配列の場合には [0] とか付加されて返ってくる. (NVIDIAの場合限定かも?)
  • サイズとして配列のサイズ(長さ)が返ってくる
  • 構造体メンバの場合は、構造体変数名が間に挟まっている
  • 構造体だからといって、1つのuniform location に集約されるわけではない

GLSLは構造体使えるから、お?とおもっていたのですが、実際のところは内部のメンバに対して1つずつロケーションが与えられているようです。

次回には、Uniform Blockでのリフレクションのチェックをしてみたいと思います。


最近のGLSLでは色々廃止になってる…

最近のGLSLでは結構廃止になっている部分が多い。ちょっと踏んだところをメモとして残しておきます。

varying, attribute 廃止

これは割と定番ですが、最近のGLSLでは廃止になってます。
代わりに in/out を使用して、シェーダーに対する入力、出力を記述していきます。

その割に、頂点入力としては glGetAttribLocation 使用して、頂点シェーダーの in で書かれた変数を取得するなど、APIとしてはアンバランスに感じます。

textureCUBEの廃止

キューブマップからのテクセルのサンプルで textureCUBEを使用していた部分が最近のものではコンパイルエラーとなるようです。自分の環境では以下のエラーが出力されました。

ではキューブマップからのフェッチはどうするのかというと、以下のような感じで良さそうです。

どうやらtextureCubeだけが廃止扱いではないようです。続く。

texture1D廃止

texture1D, texture1DProj, texture1DLod, texture1DProjLod が廃止になっています。代わりに texture 関数を使えとのことです。

texture2D廃止

texture2D, texture2DProj, texure2DLod, texture2DProjLod が廃止になっています。代わりに texture 関数を使えとのことです。

texture3D廃止

texture3D, texture3DProj, texture3DLod, texture3DProjLod が廃止になっています。代わりに texture 関数を使えとのことです。

shadow系の廃止

shadow1D, shadow2D, shadow1DProj, shadow2DProj, shadow1DLod, shadow2DLod, shadow1DProjLod, shadow2DProjLod が廃止になっています。

デフォルトのグローバルで使えた各変数

これらのものが廃止扱いになっています。あまり使うことはなかったかもしれませんが、たまに使われているケースがあってこのことを知らないと悩みます。
gl_FragColor もまた廃止なので注意が必要です。


(OpenGLの)シェーダーバイナリ(GLSL Binary)

そういえばOpenGL 4.xでシェーダーのコンパイル結果をバイナリとして取得、ロードできる機能が追加されたのですが、当時ドライバがまだ対応していなくて見送っていました。ようやくこれについて調べてみることが出来たので、ここに書いておこうと思います。

まず、DirectXではシェーダーをコンパイルすると各ベンダで共通なシェーダーアセンブリ状態になります。このアセンブリ仕様はDirectXで決められているためにベンダ間で共通データとなります。

OpenGLのGLSLをコンパイルした結果はどうなるのか、これが気になるポイントでした。
DirectXのようにベンダー間で共通のデータとなるようなら便利に使えそうです。また同機能ということでOpenGLがDirectXに追いついた!とも見えるのではと思います。

そこで手持ちの環境で同じシェーダーをコンパイルしてバイナリデータを記録してみました。
ここで用いたのは下記のシェーダーです。

このシェーダーをNVIDIA(Geforce)のボードが刺さっている環境、AMD(RADEON)のボードが刺さっている環境でそれぞれ実行してみました。

この部分はファイル先頭なのですが、この時点からして既に全く違います。
AMDのほうではシェーダーのソースがほぼ見えるのに対し、NVIDIAのほうはそんな様子はありません。
ファイルを適当に中間部分まで表示させてみると以下のようになります。

AMDのほうはよくわからないデータが入っているようです。一方NVIDIAのほうではシェーダーアセンブリっぽいものが格納されているのがわかります。

AMDのほうはバイナリファイルにELFという単語が見えますし、GLSLソースコードが入っていたりとあまりシェーダーバイナリっぽくありません。NVIDIAのほうもNVIDIA拡張のGLのシェーダーアセンブリとして書かれているようで、両者全く違うというのが明らかになったかと思います。

DirectXもシェーダーバイナリはいわゆるアセンブラなのでこの点を考えるとNVIDIAのやっている方が近い感じだと思います。コンパイルされてアセンブリになっているという点が一緒というわけです。
一方でAMDのほうはソースが入っているだけのようにしか見えません。

これらの結果からやはりOpenGLのシェーダーバイナリは実行環境でのシェーダーコンパイル結果のキャッシュデータとしての価値しかなさそうです。実行環境が決まらないようなプログラムではGLSLソースコードを保持しておくしか手がなさそうです。