ストリームアウト一覧

新しめのTransform feedbackの使い方

OpenGL transform feedbackの使い方で最近は少し変わって便利に使えるようになったようです。前回、書き込んだプリミティブ数をCPUで取得していましたが、これが不要となるようです。いわゆるDirectX の DrawAuto っぽいものがOpenGLでも実現できるようになりました。

使い方は、以下のような感じです。ポイントは GL_TRANSFORM_FEEDBACK のオブジェクトを使うというところです。ここに対してバッファの制御をさせるようなそんな感じです。バッファのバインドが GL_TRANSFORM_FEEDBACK_BUFFER に対して行うところもポイントです。

ここの transformFeedback 変数は、 glGenTransformFeedbacks 関数で準備しておきます。
クエリを発行する部分が消え去って、すっきりとした印象を受けます。
 ただこのglDrawTransformFeedback関数ですが、最近のOpenGL 4.0 をサポートするドライバがインストールされていないと使えないようです。4.0をサポートしていても使えないことがあるとか。ただ GL_ARB_transform_feedback2 拡張に本関数は定義されているようです。


Transform Feedback で GPU Particle っぽいもの

OpenGLの transform feedback を使って GPU パーティクルっぽいものにチャレンジしてみました。以前、transform feedback を試していたときは、ストリームアウトばかり気にしていて、せっかくのGPUで演算が完了できるという点を見落としていました。

今回は VBOを2つ用意してこれをピンポンすることで頂点および情報の更新を行っています。
CPUからは初期情報だけは送り込むものの、その後の頂点の情報に関しては無関係です。 Transform feedback バッファに書き込まれた個数を取得して、描画命令の発行を行うくらいにとどまっています。

実装で大変だったこと

各1スプライト単位でカラー情報を保持させていたのですが、32bitのUINT でデータを送り込んでいました。しかし、feedback バッファに書き込む際には float4 になっており、2度目以降のカラー情報がおかしな状況となっていました。これに気づくまで結構かかりました。transform feedbackを使ってデータを書き込む際には1要素はfloatである、ということにしておいて、統一をとっておいた方がよさそうです。
結局、位置(vec3)、カラー(vec4)、速度(vec3) という情報を書き込むことにして、なんとなく実現できました。

そのほかの注意点としては、シェーダーのリンク前に glTransformFeedbackVaryings で出力変数を指定しておくことでしょうか。

更新&描画時の処理としてはこんな感じです. 更新フェーズではピクセルシェーダーで書き込みを行わないので無効化したり、頂点数を知るためにクエリを発行していたりします。

やっていることは、ストリームアウトした物を描画指示しているだけです。
今までのブログの中では常にストリームアウトしたものを描画しているだけで、今回はこのストリームアウト先が描画フレーム毎に入れ替わるのが新しい点になります。だからこそシェーダーだけで位置更新できるのですが。

補足

どうやらこのようなTransfeedback の使い方は、レガシーな部類なんだとか。最近はもっと使いやすいようにGL拡張が追加されているとのことなので、これもちょっと調べてみようと思います。
 古くさいやり方だからなのかもしれませんが、意外とCPUの占有率が高かったことが気になりました。しかもカーネル時間のほうだったので、書き込み数取得のところが負荷となっているのかも?と思っている次第です。

今までのOpenGL ストリームアウト記事

OpenGLでのストリームアウト その1
OpenGLでのストリームアウト その2
OpenGLでのストリームアウト その3
OpenGLでのストリームアウト その4
OpenGLでのストリームアウト その5


OpenGLでのストリームアウト その5

AMD(ATI)で動くOpenGLのストリームアウト(transform_feedback拡張)が一応できました。GL_EXT_transform_feedback のエクステンションを利用します。

準備するもの

  • 頂点データのためにVBOを使えるようにすること
  • GLSLによるシェーダーを使えるようにすること
  • クエリー発行できること

シェーダーの準備

GLSLシェーダーを準備します。
glAttachでオブジェクトを設定し、glLinkProgramまで終わらせておきます。

その後そのシェーダーからストリームアウトするのに必要なvarying変数を設定します。
変数名による配列を準備して、
それをglTransformFeedbackVaryingsEXTにて設定します。
この例を下記に示します。

ストリームアウト実行部

シェーダーをセットするほか、いくつかの設定を行います。

ストリームアウトするため、ラスタライズ処理の無効化をします。
また、書き出す先のVBOをバインドしておきます(streamOutIdが対象VBOです)。
続いて、ストリームアウトの開始を宣言し、同時に書き込んだプリミティブ数を取得するためのクエリを発行しておきます。

この後、通常の描画と同じように実行します。
終了時には設定を戻します。以下に例を示します。

各種終了を宣言し、クエリの結果を取得します。
ここでプリミティブの数を取得しないとその後結果をうまく使うことができません。
OpenGLにはDrawAutoは存在しないためです*1

この後描画するのであれば、GL_ARRAY_BUFFERにバインドして通常と同じように描画できます。
ただし、インデックスバッファと併用できるかは現在未確認です。
頂点の増減がGSで行われてしまうため(行われる可能性があるため)です。
頂点を増やさなかったら、インデックスバッファ使える可能性はあるのかも。

*1 : transform_feedback2拡張らで含まれるらしいですが

NVIDIAで使えない理由

glBindBufferOffsetEXT のAPIだけが取得できません。
取得しようとするとNULLが返ってきます。
そのため、同様の機能を探すとglBindBufferOffsetNVとなるため、
それだったらNV拡張のほうを使うか、ということになるわけです。

多くの部分でEXTと共通ですが、ストリームアウトするvarying変数らの設定が異なります。
NVIDIA版では、変数のLocationIdで設定するようになります。

感想

とりあえずNVIDIAの今後のドライバでこのAPI取得ができるようになるなら、
どの環境であれEXT拡張に統一できるんですが・・・。
また、一部のAPIが欠落しているのにもかかわらず、GetString( EXTENSION )でサポートしている旨を返してきてほしくはないですね。


OpenGLでのストリームアウト その4

今までのものは GL_transform_feedback_NVのやつでやっていた。
しかし、今回AMDのRadeon 5450環境を手に入れられたのでこちらについても
ちょっと試してみた。GL_transform_feedback_EXT をサポートしているようなのでこちらを使うことになりそう。

APIの置き換え(その1)

NVIDIA AMD
glBeginTransformFeedbackNV glBeginTransformFeedbackEXT
glEndTransformFeedbackNV glEndTransformFeedbackEXT

こんな感じで単純に置き換えておけば、NV->EXT使用になる

APIの置き換え(その2)

glTransformFeedbackVaryingsNV は glTransformFeedbackVaryingsEXT を使用することに。
中身が違うので単純に置き換えてはいけない。

以前 ”GLchar**を受け取る!?ヘッダ正しいですか?”と書いていた部分が間違っていたことに気づいた。

  • EXT版ではGLchar**を受け取る。
  • NV版ではGLintを受け取るべき

という差異があった。これが仕様。

EXT版では、varying変数名を設定できるが、
NV版ではvarying変数のロケーションIdを受け取る
という仕様の違いがあったためであった。

NVIDIAとAMDと両方のデバイスをさわってみて初めて納得がいった。

感想

やっぱりOpenGLのTransform Feedback(StreamOut機能相当)は情報が少ない。
使っている人が少ないのかもしれないので、これからがんばる人のために
ここに情報を書いておきます。

近いうちにNVIDIAとAMDと両方で動くtransform_feedbackのサンプルを作って公開する予定です。


OpenGLでのストリームアウト その3

ようやく動作自体は出来たのでメモしておきたいと思います。
サンプルプログラムはまた後日に。

準備

  1. GLSLシェーダーをコンパイル
  2. シェーダープログラムをリンク処理
  3. glTransformFeedbackVaryingsNV でストリームアウトとして書き込むデータの設定
  4. 格納先のバッファオブジェクトを生成

描画フェーズ

  1. ラスタライズ処理の無効化(GL_RASTERIZER_DISCARD_NV)
  2. glBindBufferOffsetNV, glBeginTransformFeedbackNV らで書き込み先のバッファオブジェクトを設定
  3. 描画処理.
  4. glEndTransformFeedbackNVで終了処理.
  5. GL_RASTERIZER_DISCARD_NVのステートを戻す

描画の前後でクエリー発行しておくことで書き込まれたデータ(プリミティブ数)を取得することが出来ます。

ここまでくるのに色々と試行錯誤しましたがようやく動かせる状態にきました。
注意のポイントは以下の点だと思います。

  • シェーダープログラムのリンク後の処理
  • GLSLなのか、固定機能による処理なのか

OpenGLでのストリームアウト その2

GL_NV_transformfeedbackのspecテキスト

こう書いてあった。

なるほど、1つ疑問が解けた。
今までTransformFeedbackAttribsNVを実行していたが、
これは固定機能での描画時に使うもののようだ。
(VSもGSも存在しないときと書いてある)。

つまりGLSLでやるならば、TransformFeedbackVaryingsNVを使って設定することが必要ということ。

こちらで試してみる。
ネット探してのサンプルだけを頼りに試行錯誤していましたが、
本家ドキュメントもしっかり読まないとダメですね。反省。

その他

昨日のGLchar*とint*の差違問題はどうやらNVIDIAからglext.hを持ってくると、int*となっている。
OpenGLのサイトから持ってくるとGLchar*となっている。

とりあえずNVIDIAのほうからヘッダ持ってくるとしよう。


OpenGLでのストリームアウト その1

OpenGLを用いてもDirectX10のストリームアウト相当の機能を使うことが出来ます。この機能のためにまず最初に拡張されたのが GL_NV_transform_feedback というエクステンションです。
最近ではこれがEXTになったようですが、単純にNVから格が上がっただけとはいえないようです。
APIが変更されているからです。

EXT版で試行錯誤しましたが、うまくいかず、NVのほうでまず動作を試してみようと思います。

目標

これらの点から次のことを目標にしています。

  • Cgを用いずにGLSLを使う
  • ひとまずNV拡張のほうを使ってみる
  • ストリームアウトの結果を見るために、ジオメトリシェーダーでは入力頂点を増やして出力する
  • 出力頂点を確認してみて、動作を確認する

OpenGLでのストリームアウト

OpenGLではVertexBufferObject(VBO)に対して結果を書き込むことができます。*1

他にも、tranform_feedback2, 3とかあったりしますが、
これらはOpenGL4系で追加だそうです。

*1 : バッファオブジェクトであれば書き込み先に出来るようです

GL_EXT_transform_feedbackの不審点

glBindBufferOffsetが関数実体を持っていなさそう。
glBindBufferOffsetNVだと関数実体は取れる。
こんな状態なのに、GetStringでエクステンション名は取れてしまう。
不思議です。

GL_NV_transform_feedback 拡張の罠

今のところいくつかの怪しい点で動作せず状態です。

  • glext.h内でglTransformFeedbackAttribsNVが const GLchar* *varyings の引数になっている。
    • ExtensionRegistryでは、ここはGLint*なはずなのに。

現時点での動作不良点

  • 書き込んだプリミティブ数が0のままで取れない…。
    • 書き込み先バッファは何かデータがかかれているようだが。
    • (glMapBufferのREAD_ONLYにて値を確認)

完全に動作するサンプルを検索しても見つからないので苦労しています。
NVIDIAではCgつかったサンプル例となっているし、そのほかもCg使ってしまっているし。
GLSLでやった例ってホント見あたらず。