「 2010年09月 」一覧

テクスチャとサンプラーステートの関係


テクスチャとひとことで言った場合、この中には2つの要素が含まれていると思います。
1つは、ポリゴン表面に貼りたい画像のデータ。
そしてもう1つは、その画像に適用するフィルタ設定やUV設定といったの各設定情報。このうち後者はテクスチャフィルタやラッピングモード(アドレッシングモード)とも言われるため
テクスチャの情報としてまとめられてしまうことが多いと感じます。
特に昔からのOpenGLではテクスチャオブジェクト内にラップモードとフィルタ設定を行っておき、
テクスチャの使用時にはその設定が有効になるという挙動を示します。

DirectX9においては後者の設定はテクスチャの処理するステージに対して設定するものとなっています。
SetSamplerStateメソッドのヘルプを見てみても、サンプラー番号といわれる部分はサンプラステージインデックスと表記されています。
HLSLシェーダ内にレジスタ指定できますが、そのときの register(s0);という”s”の部分については、おそらくステージの意味合いなのではと思います。

しかし近年、DirectX10(D3D10)以降これらの関係がAPIの上から変わりました。
前述の各設定情報がサンプラーオブジェクトとして分離されました。
ハードウェアの面から考えると、画像が置いてある部分の内容と、そこから読み出すときのフィルタ設定は確かに分離でき
こちらのほうが自然な印象を受けます。
この時代においても、レジスタ指定の部分では従来どおり register(s0); ですが、
このときの”s”の意味合いは、サンプラーの意味を持つ”s”へと変わっていると思います。

また、OpenGLのほうでもテクスチャの設定関連が見直され、
新しいOpenGL*1では、テクスチャとサンプラーとそれぞれ別のオブジェクトとして使うことが可能になってきました。
これらにおいては、glGenSamplers, glBindSamplerといった生成、割り当ての関数が追加されています*2

新しいものだけを見れば、DirectXもOpenGLも同じ流れになっていてすっきりしました。
しかし、気になる点も1点あります。
それはこのサンプラーの設定はサンプラーの番号に対して設定するという方法しかないという点です。
HLSLのエフェクト(.fx)を使う上では、サンプラーステートと参照するテクスチャを結びつけて記述が出来ますが、
それぞれのシェーダーを別々に設定するような機構では、この設定は出来ないようです。
まだ見つけられてないだけかも知れませんが、OpenGL(GLSL)ではそういった機構すらないように思えます。

これらの点から、テクスチャの割り当てとサンプラの割り当ての番号は一致するように実装しておくのがいいのかなと思いました。
テクスチャの割り当ての番号自体は、シェーダーリフレクションを使えば求められます。
DirectX9については、そもそも同一のステージ番号に閉じ込められていますし、
旧OpenGLではテクスチャオブジェクト内にまとまってしまっています。
全てにおいて統一の取れたものにするには、この方法しかないのでは?と考えています。

*1 : OpenGL3.2以降だと思われる

*2 : GL_sampler_objects


glGetActiveUniformの挙動


AMD製グラフィックボード搭載環境時に glGetActiveUniform関数が正常動作しない模様。
Catalystドライバが悪いのかも知れないが。
uniform sampler*のデータが取得できない。
有効なUniform変数の個数は意図通りなのに、sampler用と思われる部分でLocationIdが -1 という値が返ってくる。

NVIDIA製グラフィック搭載環境の場合、この問題は発生せずuniform sampler*を使用しても、
正しくLocationIdの値を返してくれる。
また、変数名をソートしてその順序でLocationIdが割り当てられるようだ*1
OpenGLの場合、シェーダー変数へのアクセスはLocationIdを介して行うので、
名前指定して値のセットということはDirectXのEffectと違い行わないのでこの最適化はしなくてもと思ったり。
それでもシェーダーコンパイル時にやってしまうという点は評価できるけど。
毎回名前からLocationIdとってきて値のセットというようなプログラムコードを書く場合にはこの部分は有効に効いてきそうだ。
逆に言えば、この最適化を期待したコードを作ってしまうと、他の環境やドライバでは速度に問題が発生するかも。

*1 : ドライバ 259.09にて確認.

追記

AMD製グラフィックカード搭載のWindowsXP環境で試してみた。
すると、sampler*に関してもただしくLocationIdが取得でき、定数名はソート済みになっていた。
問題となっている環境は Windows7 64bit, Catalyst 10.7 の状況。ここに何か問題があるのかもしれない。

追記2

Windows7 64bit環境の方でもコードを書き直ししていたら、
正常にsamplerの場合でも値がとれるようになった。
もしかしたら大本のソースがバグっていたのかもしれない。

まとめ

単純に自分のミスだったのかもしれないなぁ…。
glGetActiveUniformに問題はなし、ということで。

また、AMD製グラフィックボード時でも、
シェーダー定数群においては辞書順にソートされて結果が列挙されました。

結果がソート済みになることは仕様に入っていなかったと思うので、
この部分について必要ならば自前ソートをかけておく、というのが
よいかなと思います。


CPU書き換えテクスチャ転送


DirectX9で、CPU上でごりごりとテクスチャ処理して
VRAMに転送し、普段使うのはその転送後のテクスチャという方式を考えてみました。CPU上のテクスチャは D3DPOOL_SYSMEMで作り、
VRAM上のテクスチャは D3DPOOL_DEFAULTで作るという方法です。
この転送には、IDirect3DDevice9::UpdateTexture メソッドが使えます。

少々メモリは食いますが、
CPUからの操作のやりやすさと、実行パフォーマンスという点では
この方法は結構いけてると思っています。

で、1つ手間取った部分がありました。
デバイスロスト等で再度テクスチャを転送しなくてはいけないときに、
何も考えずにUpdateTextureをしても反映されませんでした。
どう対処するかというと、転送元のテクスチャにダーティフラグをつけておかなくてはならないようです。

これには、AddDirtyRect メソッドがあります。
とりあえずロック&書き込みしないような転送前には全領域をダーティとしてマークしておけば、UpdateTextureで正しく送ることが出来ます。

知っていれば、なんてことはないネタですが
自分の防備録として日記に残しておきました。


シェーダーリフレクション [DirectX10]


シェーダーの中身がどのようなものが詰まっているかを取得するための方法です。
なかなか情報がなくて大変でした。
やりたいこととしては、シェーダーのバイナリファイルから
どのような変数およびデータ型がどの位置に存在するのかと知る方法となります。D3DX9には存在していたID3DXConstantTableのようなものを
D3D10ではどうやるかという感じになるでしょうか。

サンプルソース

各種リフレクションのインターフェースを取得して、中身を調査します。

実行結果

この結果は、BasicHLSL10のシェーダーコードを元にしています。
どうやら、グローバルにおいた無名の定数バッファは$Globalsという名前で配置されるようです。

また、関数の引数で uniform と宣言したものについては、
$Paramsという名前の定数バッファとして処理されるようです。

さらに、Descでの値は$Globalsの値を考慮していないっぽいです。

まとめ

いろいろと注意点はありますが、これでコンパイル済みシェーダーファイルを利用する目処が立ちそうです。
また、このリフレクションはCore APIに搭載されているため、
別途のd3dx10_**.dllに依存しないのもいい点だと思います。


ホストオンリーネットワーク


いろいろとドメイン関連の実験をしていて、
閉じたネットワークを構築したかったため、ホストオンリーネットワークの機能を使用した。

ただ、ゲストとホストの1:1対応であれば何も考えずに使えるのだけど、
ゲスト間で通信し、なおかつドメインのようなDHCPやDNSを動かしているようなときには、通常のままではうまく動かない。

なぜならば、vmwareが提供しているDHCPサービスが動いているからである。
今のような、内部にDHCPがいるような場合、実験するような場合では、このサービスを切って行わなければならない。

  • スタートメニュー/コンピュータの右クリックで管理
    • サービスの中
      • VMware DHCP Service

これを停止にして、作業を行うこと。
一連の作業が終わった後ではまた元に戻しておくのがよいと思われる。

別の方法

仮想ネットワークの設定というツールで設定することも可能。
対象となる、ネットワークアダプタを選択して、
”ローカルDHCPサービスを使用してIPアドレスをVMに配布する”
という部分にチェックが入っているようならこれを外してあげればよい。

状況に合わせて、サービス停止orアダプタの設定変更どちらかを選べば良さそうな感じである。


ドメインサーバーを2003からWin2008R2へ


2003R2で構築しているドメインに、2008R2のサーバーをメンバーサーバーとして追加して、その後昇格させる方向でドメインサーバーのアップグレードを図ってみた。

メンバーサーバとして追加

まず、サーバー構成マネージャでドメインサーバー用の設定をした後で、
dcpromoを実行したところ、以下の失敗メッセージが出てきました。
「このActive Directory フォレストにドメインコントローラーをインストールするには、最初に”adprep /forestprep”を使用してフォレストの準備をする必要があります。」

よって、Win2008R2のディスク内 support/adprep にあるツールを実行します。

対象の2003R2のサーバーで2008R2のディスクを挿入して、

その後、”C”キーを押してからEnterキーを押す。
すると準備が開始されます。
ここで”C”を押さずにEnterを直接押してしまって、あれ?って状態になりました。
メッセージはきちんと読みましょう。

このまま、再度dcproom.exeの実行するとまた別のメッセージが出てきます。
「このActive Directory フォレストにドメインコントローラーをインストールするには、最初に”adprep /domainprep”を使用してドメインの準備をする必要があります。」

よって、同じように対象のWin2003R2サーバーで先ほどのツールを実行してみました。
しかし、今度はツールが正常完了せず、以下のエラーメッセージが出てきました。

「Adprep は、ドメインがネイティブ モードではないことを検出しました。」
どうやら、以前環境構築したのが旧バージョンでも問題ないモード(混在モード)で動いていたらしく、
Windows2000ネイティブ以上の機能で構成しないとダメのようです。

  • “Active Directoryユーザーとコンピュータ”で、機能レベルを更新
    • ドメイン部で右クリック「ドメインの機能レベルを上げる」を選択
    • 現在”Windows2000混在モード”→”Windows Server 2003″
  • コマンドラインで adprep /domainprep /gpprep を実行する
  • adprep /rodcprep を実行する。
    • これを忘れると、あとで警告が出てくる。

ここまで処理して続いて 2008R2側でメンバーサーバー追加の再チャレンジ。
警告および確認が出てくるので、問題なければ続行。

これらの処理が完了すると、2008R2でのドメインサーバーがメンバとして登録されます。

新しくドメインコントローラーとするために

メンバーサーバーとしての登録が終わったので、ドメインコントローラーへ昇格させます。

操作マスタの役割を転送します。

  • ActiveDirectoryユーザーとコンピュータ
    • 右クリックで操作マスタを開く
      • 各マスタを旧サーバーから新サーバーへ転送する。
      • (RID, PDC, インフラストラクチャ)

旧ドメインサーバーのグローバルカタログを無効にします。

  • ActiveDirectoryサイトとサービス
    • Sites
      • (ドメインコントローラのあるサイト)
        • Servers
          • (ドメインコントローラー)
            • NTDS Settings

とたどっていき、NTDS Settingの部分で右クリックしてプロパティを開きます。
そのなかで、グローバルカタログという項目にチェックボックスがあるので、
このチェックを外すことでグローバルカタログを無効化できます。

その後、旧サーバーでdcpromoを実行して、降格処理を行います。
ActiveDirectoryの削除が実行され、単なるメンバーに降格します。

参考

MSのサイト以外のところで参考にした部分。
http://pnpk.net/cms/archives/373
http://norimaki2000.blog48.fc2.com/blog-entry-507.html
http://blog.tsukuba-bunko.jp/ppoi/archives/2008/04/3.html

この部分を参考にさせていただきました。
ありがとうございました。


VT-d と ESXi と


Intel Core i5をVT-d目的で購入。
VT-dを使える環境で ESXiでPCIパススルーするために。

しかしながら、vSphere Clientで確認してみても、
ホストが対応していないと出てくる。
原因が分からずにいろいろと調べた結果、以下のことが分かった。

これが盲点だった。
手元のメインESXi環境である、”GA-H55M-UD2H”ではBIOSでその対応がなされていないものだった。
もう1つのPC環境ではASrockのマザーで、こちらではVT-dの項目がある。
こちらでは、以前の日記に書いた実験を行ったので、動作はやはり環境依存っぽい。

どうもBIOSでVT-dの項目記載が無いような状況では、そのマザーは対応していないと判断してもよいのかもしれない。

今のところ、ネットで調べてみてもIntelのマザーくらい対応していないのかも。
たまたまASRockマザーでは動いていたのは運がよかった(いいもの当てた)としか言いようがない。

もし、VT-d有効で嵌っている人がいたら、この確認をやってみよう。


レンダーバッファとレンダーテクスチャ


Framebuffer Objectで設定するレンダーバッファとレンダーテクスチャについて。どこかの記載で、レンダーバッファのほうがレンダーテクスチャよりも
高速に動く余地があるということが書いてあった。
これは以前の日記でも書いたとおり。ここでFBOにはレンダーテクスチャとレンダーバッファとそれぞれ設定するための関数が用意されているが、
同時に設定したらどんなことが起こるのかが疑問であった。
レンダーバッファとレンダーテクスチャ、両方が同時に使えるのだろうか?

実験

今回は、以前のデプステクスチャ、デプスレンダーバッファのコードがあったのでこれを利用して試してみました。

  1. レンダーテクスチャを用意
  2. レンダーバッファを用意
  3. framebuffer objectを用意
    1. カラーに関してはレンダーテクスチャをセット
    2. デプスについてはこの時点ではセットせず

この後、メインループに入ったところで
何らかのボタン押下により、レンダーバッファをアタッチ、もしくは、レンダーテクスチャをアタッチさせる。
このとき、Fboの状態取得関数を用いてどのようになっているかを調査する

ちなみに今回、デプスのアタッチなので正常にアタッチ状態は
画面の描画が正常であることを前提にして取得できる値を確認した。

結果

Case 1:レンダーテクスチャをアタッチして、レンダーバッファをアタッチ

このとき、状態は以下のように遷移した。

Case 2:レンダーバッファをアタッチして、レンダーテクスチャをアタッチ

このときは以下のように遷移した。

まとめ

実験の結果から、アタッチできるのはレンダーバッファかテクスチャかどちらか1方であることがわかった。
すでにアタッチされている場合、それは解除され新しく設定したほうが、
対象先の有効なアタッチ先となる。

個人的にはレンダーテクスチャのアタッチだけでいい気もする。
パフォーマンスもそんなに損なわれないのであれば、あるいは許容できる範囲なら、
Renderbufferを使用せず、RenderTextureだけで実装した方が
シンプルにまとまっていい感じになるような、そう思います。


FramebufferObject挙動違い -その3-


今回は今までの総合しての解決編になります。
デプスとステンシルを1つのテクスチャでOKとし、
さらにNVIDIAとAMDと双方で問題なく動く、という解法になります。

実装コード

やはりテクスチャの作成がポイントでした。

内部フォーマットに D24S8の指定を行い、
ピクセルフォーマットとして、GL_DEPTH_STENCIL_EXTを設定
これに対応する型として GL_UNSIGNED_INT_24_8_EXT を設定

この組み合わせがすべてを解決してくれました。

このデプステクスチャがうまくいったので、
書いた値をファイルに書き出してみました。
こんな感じに記録されており、何らかの値がかかれているのがわかります。
値はちょっとわからないですが、形状からおおむね正しくデプステクスチャに書き込んでいるものと推測できます。

取得する際にはこんな感じで。

GL_DEPTH_STENCIL_EXT GL_UNSIGNED_INT_24_8_EXT
GL_DEPTH_COMPONENT GL_UNSIGNED_INT

組み合わせを間違えるとうまく値がとれません。
デプス確認するだけなら、下段の書き出し方法のほうが見やすいかも。


FramebufferObject挙動違い -その2-


AMD製グラフィックボード時でも正常動作できる組み合わせを発見しました。テクスチャの生成時がポイントです。

解決方法(AMDオンリー)

これを従来通りにFramebufferObjectにデプス用としてアタッチしてあげると、
正常に動作することができました。

問題点

このコード、AMD製カード搭載時はうまくいくのですが、
NVIDIA製カード搭載時にはうまく動作しないのです。

解決方法

前述の方法を発展させて、NVIDIA/AMD双方で動く設定を以下に示します。
やはりテクスチャの作成がポイントです。

このコードを見て、「あぁNVIDIAはデプス D24以上しか対応してなかった」と思い出しました。
D16とかNVIDIAでは使えないんですよね。

懸念点

この方法だとStencil使いたくなったときに、もう1枚テクスチャが必要に。
さてさて、どうするか。
packed depth stencilの拡張が何かあったけど、これがテクスチャで使えたりしないだろうか。
要調査と言うことで。