Direct3D もうひとつのユニファイド

従来相違があって不思議と互換性が無かった
頂点フォーマットとテクスチャフォーマットが D3D10 で統一されています。
これもシェーダーが統合されたおかげでしょうか。

Direct3D9 では例えば頂点だと

 FLOAT4   float×4
 FLOAT16_4  float(16bit)×4
 SHORT4N   short×4
 UBYTE4   byte×4 (a b g r)
 D3DCOLOR  byte×4 (a r g b)
 DEC3N    (10,10,10)
 
等の専用形式があり、テクスチャにも

 A32B32G32R32F
 A16B16G16R16F
 A16B16G16R16
 A8B8G8R8
 A8R8G8B8
 A2R10G10B10

があります。上にあげたものはほぼ似たようなフォーマットですが、
それ以外のものは互換性が無くアクセスのために専用のコードが必要でした。

Direct3D10 では VertexShader も PixelShader も同じ性能を持っているので、
アクセス可能な Buffer および ShadeResource のフォーマットが同じものになって
いるようです。
(2次元圧縮の BC(DXT) 形式は除く)

その代わり頂点フォーマットでも R G B A 表記になります。
よく見ると D3D9 の A B G R 表記と逆順です。
もちろん x y z w にあわせたためだと思われます。

xyzw と rgba の統合までの道のり

・D3D8 まで ARGB → D3D9 で ABGR 追加 → D3D10 で RGBA に変更

DirectX Direct3D 10 その2

●caps

caps 情報が必要なく、どのビデオカードでも同じように動作するのが 10 の特徴と
されています。ただ全く判断が必要必要ないかといえばそうではなくて、最低限
D3D10 が動くかどうかの判別は要ります。

DX5 あたりの時代のビデオカードだと、同じチップなのにメーカーによって結構
機能が違っていました。この違いはドライバによるもので、有効になっている機能
であっても実際はドライバ側のエミュレーションだったり他の機能の代用で実現
していたのかもしれません。

D3D10 ではどのメーカーのビデオカードでも同じ用に動作することが求められて
いるのですが、カードによって実装方法が異なっていることは考えられます。
機能が有効になっていてもビデオカードによってはエミュレーション実装で実は
重い、なんて状況にもしなるんだったら、事前に caps で判断して苦手な機能を
弾ける方がいいのかもしれません。
この辺はさまざまなビデオカードが出てくるまではまだなんともいえないところです。

●GeometryShader

GS はプリミティブ単位でシェーダーを実行することができますが、さらに隣接する
プリミティブの情報も参照することが可能です。これは ADJ 系の Topology データ
が必要です。隣接するプリミティブの情報を格納した IndexBuffer や頂点情報を
作成しておくわけです。(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ 等)

なのであらかじめ対応するデータ形式への変換が要ります。頂点情報が重複するので、
本当に隣接情報を受け取りたいだけなら基本的に Indexed だけ使うことになるで
しょう。

例えば Triangle なら隣接込みで 6頂点入力になります。この余分な頂点情報にわざと
関係ないデータの頂点を与えることで、プリミティブ単位の追加情報領域として
活用することができそうです。ここに面法線とか面マテリアルとか入れて GS で
合成してあげるなど、かなり使い道がありそうですね。

なお GS で頂点の追加ができますが、試してみたところ一度の出力は最大 1024 要素
(スカラー)までに制限されるようです。例えば頂点形式が

px py pz nx ny nz tu tx

だと8要素なので、1024/8= 128個。つまり 128頂点まで出力することができます。
Triangle List だと 42個分になるでしょうか。

●resource

リソース周りの構造の理解も重要なポイントです。かなりのリソースが相互に流用
可能になっています。大きく分けると Buffer と ShaderResourceView の2種類です。
シェーダー側でも Buffer として読むか ShaderResourceView として読み込むかで
命令が違います。
ShaderResourceView であっても結局 Load() を使えば offset 指定で直接読めるので、
できることは cbuffer とあまり変わりません。VertexShader のみ cbuffer に
使えるリソースが ConstantBuffer だけに制限されています。

なので VertexShader だけは c0~ 系命令のアクセスが最適化されているけれど、
他の Shader では内部で Load() に置き換わっているのだろうか、とかちょっと
思いましたがそんなことは無いよいうです。

サンプルでもこの辺の比較プログラムが存在するので、どれくらいパフォーマンスに
差が出るかは試すことができます。

ShaderResourceView を使うと FORMAT によるさまざまな形式のデータを読み込め
ますがその代わり配列相当なのでフォーマットの異なる構造を作れません。
(tbuffer としてはアクセスできる)

Buffer を使うと構造を定義できますが、int, float, half 等の組み合わせなので
DXGI_FORMAT のような柔軟なデータ形式では直接宣言できなくなるようです。
(R8G8B8A8とか)
ちなみに subresource は ~DX9 でいう Surface のことのようです。

PS3 PPU は速いのか

PS3 の CPU である Cell は 3.2GHz で動いています。
Cell は複数の CPU core の集合体であり、中には PPU ×1 と SPU × 8個の
合計 9個の CPU core が含まれています。
ただし PS3 版 Cell では SPU の個数は 7個に減っており、合計 8 core です。

Cell が圧倒的なパワーを持つといわれているのはこの 8個 (7個) の SPU を持って
いるからです。SPU だけで 8個 (7個) の異なるプログラムを同時に走らせることが
できて、特に大量の浮動少数演算でその威力を発揮します。

これらの複数の SPU を統合整理するのが PPU の本来の用途だといわれています。

PPU 自体は 64bit の Power アーキテクチャで、PowerPC と完全に命令セットの
互換性があります。Linux を比較的容易に走らせられるのもこのためで、
PowerPC 用の一般のバイナリパッケージをインストールすることが可能です。

PowerPC G5 (970) が最大でも 2~2.5GHz 前後だったことを考えると、3.2GHz で
動作する Cell は PPU だけでもかなり高速な印象を与えます。でも実際はそれほど
でもありません。

もともと PowerPC は比較的効率の良い動作をする CPU でした。ヒット率の高い分岐
予測やアウトオブオーダー実行、レジスタのリネーミングなど、パイプラインの
無駄を高度なアルゴリズムで実行時に回避します。またそのような方向で進化して
きました。割と一定した速度の出る、どんなプログラムでも速度が落ちにくい CPU
アーキテクチャだという印象があります。

逆にそれが複雑な設計を招いているのか、比較的高クロック化の妨げになっていた
と考えられます。

Cell や Xbox360 の CPU で使われているこの新しい CPU Core は、これまでの
PowerPC の路線とは完全に逆向きの設計が行われているようです。

つまり、高クロック化の妨げとなるような複雑な設計はやめて、シンプルかつ小さな
core にすること。その代わり速度を高クロック化とマルチコア化で稼ぐというもの
です。

もちろんアウトオブオーダー実行できないと、パイプラインは依存が発生する
たびにスカスカになることが考えられます。これを回避するための方策として用い
られているのがマルチスレッド化です。PPU 1つでも、依存関係の無い 2つのスレッド
コンテキストを持つことで、パイプラインの無駄を回避しようというわけです。

Pentium4 の HyperThread と似たようなものといえるかもしれません。だけど HT
よりもスレッドへの依存はより積極的だと考えられます。

まとめるとこんな感じになります。

・高クロック化の妨げになる複雑な高度な回路は無くする
  → シンプルになるので高クロック化しやすい
  → 複雑な回路が減って面積が小さくなる
  → CPU の動作効率が落ちる

・落ちた動作効率をカバーするために次の新たな技術を導入する
  → 高クロック化
  → マルチコア化
  → マルチスレッド化

動作効率の低下は、CPU を複数積んで、クロックをあげることで回避せよと。
またパイプラインや演算ユニットの無駄はスレッドで回避しろと、ある意味力技に
見えます。

これらの仕様を考慮すると、CPU の癖というか性格としては、次のような傾向になる
ことが予想できます。

  ピーク性能は高いがかなりピーキーであり、性能を安定して引き出す
  のは難しい。

・コンパイラ依存率が非常に高くなる
  → 実行時の命令順最適化が行われないので、あらかじめコンパイル時に厳密に
   最適化しておく必要がある。そうでないと性能が出ない。

・スレッドを2本走らせないと100%性能を発揮できない
  → 別スレッドでパイプラインを埋めることが前提の設計なので、スレッド1本
   では無駄が発生している。

つまり同じ Power アーキテクチャといえども、これまでの PowerPC とは全く正反対
の性格付けになっているわけです。

ちなみに Xbox360 の CPU は、SPU が無い代わりに Cell でいう PPU 相当が 3つ
入っています。2次キャッシュの容量とベクトル専用命令の追加に違いがありますが、
CPU core そのものの特徴や傾向は全く同じだと考えられます。

Xbox360 の CPU も、100% の性能に近づけるためにはそれぞれの core で 2スレッド、
合計 6スレッド分のプログラムが必要だということです。

PS3 Linux で PPU が 2個の CPU に見えるのは、このように HT のような2本の
スレッドが走るためです。

PS3 Linux で実際に使ってみて結構動作が遅く感じるのは、もちろん Cell のうち
ごく一部しか活用されていないこともありますが、従来の PowerPC 用コンパイラ&
バイナリでは相性が悪いというのも若干影響しているかもしれません。

PS3 をネットラジオプレイヤーに

Linux を使って Playstation3 をネットラジオプレイヤーにしてみました。
ゲームをしていないときの PS3 活用方法として意外に便利かもしれません。

PS3 に Fedora Core 5 を full install するとさまざまなアプリケーションが
入りますが、そのままでは mp3 の再生ができないようです。
ネットで調べると、この辺いろいろと設定したりソフトウエアやプラグインを
インストールしたりと少々手順が必要です。
ところが同じ Linux でも、FC5 ではなく Debian の方を使うと
特に何もしなくても再生することができました。

もっとも、PS3 の場合音楽再生だけなら GAME-OS (XMB) でもできるので、
ここではネットラジオを再生してみます。

以下 PS3 上の Debian (Etch) の設定です。

● サウンドを有効にする

Debian では最初 PS3 用のサウンドドライバが読み込まれていないため、
「modprobe snd_ps3pf」を実行する必要があります。

この読み込みを自動で行うために、/etc/modules に snd_ps3pf という行を追加
します。テキストエディタで編集してください。追加後下記のようになりました。

——————–

gelic_net
loop
ps3pf_storage
snd_ps3pf

——————–

設定したら一度再起動しておきます。

●データの再生

これで一般的な音楽データは再生できるようになります。
例えば mp3 ファイルの再生も OK です。
ただし標準で立ち上がる「動画プレイヤー」(totem)だとかなりノイズが乗るので
Rhythmbox の方をお勧めします。

●インターネットラジオの再生

Rhythmbox の演奏メニューから「新しいインターネット・ラジオ局」を開きます。
ここに再生用の URL を入力すれば OK です。
入力した URL がラジオ局一覧に入るので、ダブルクリックで再生できます。
URL は .pl や .m3u 等のファイルをエディタで開くと確認できます。

もしくはブラウザでネットラジオのページの再生ボタンを押して、
アプリケーションで開く先を Rhythmbox にします( /usr/bin/rhythmbox )。
これで簡単にラジオ局一覧に取り込めるようになります。
SHOUTcast の再生もできました。

標準のブラウザだと開く先のアプリケーションをその場で指定できないので
「動画プレイヤー」(totem) が起動してしまいます。
先に Firefox を入れておいた方がよいでしょう。
ターミナルから下記のように実行して追加できます。

 $ su
 # apt-get update
 # apt-get install firefox
 # exit
 $ firefox

●他のプレイヤーの追加

Rhythmbox ではなく他のプレイヤーを使うこともできます。
この場合ブラウザでクリックしてプレイヤー選択するだけで再生できます。
例えば xmms を追加するなら Firefox と同じように

 # apt-get update
 # apt-get install xmms

と実行します。これでインストール完了です。メニューから呼び出せます。

Debian の場合 Live CD を簡単に作れるので、CD から起動できるネットラジオプレイヤー
なんてできそうですね。

DirectX Direct3D10

GeForce8800 のドライバが出た(?)ので早速 D3D10 を試しています。
ずっと DirectX SDK には Direct3D10 の Preview が付属していましたが、
RTM した December でもマニュアルが融合しただけで D3D9 も D3D10 もどっちも
入った状態なのは一緒です。

これまでも、DirectX9 に入ってた DirectInput はずっと DirectInput8 の
ままだったし、DirectSound も DirectSound8 でした。
なので Direct3D も、DirectX 全体のバージョンとは独立したナンバリングが
行われたようです。

といっても、もともと DirectX の初期の頃は各コンポーネントごとにばらばらに
インターフェース番号がついてました。

で、確か DirectX7 のときに SDK 全体の番号と各インターフェースの番号の統合
が行われました。ほぼ全部のインターフェースの番号を SDK の番号にあわせたわけ
です。

だから元に戻ったようなものでしょうか。

D3D10 はインターフェース周りからフォーマット名のシンボルから使い方まで
Direct3D9 と全く違ったものになってます。
結構大変に見えるかもしれませんが、DirectX7 まではだいたい 1年に一回
こんな感じで大幅な刷新があったんですよね。

とりあえず D3D10_DRIVER_TYPE_HARDWARE でちゃんと高速に動作しました。

API セットは Fixedパイプが無くなってかなりすっきりしたはずなのですが、
その代わり汎用性が増して自由度があがったためか、インターフェースの種類も
かなり増えています。おかげで一見しただけだと全体の構造がわからなくなって
しまいました。
例えばテクスチャの読み込み1つ見てもやるべき手順が増えてます。

また従来は目的毎に API があったため何をやるのかは割と明確でした。今では
ライティングもマテリアルもフォグも何から何まで Shader の実装依存になった
ために専用の API や構造体は無く全部汎用です。

逆に Depth, Stencil あたりの判定の流れは変わっていないので、レガシーに
感じてしまいます。

設定やパラメータの渡し方も大幅に変更になっています。効率優先でできる限り
バッファに置く構造となっていて、非常に徹底したものです。
いまのビデオカードは RAM 容量もかなり多いので、これを活用する構造にシフト
したのでしょう。コマンドストリームに乗せるものは最小限です。

・マニュアルのミス?
 December でマニュアルの D3DX10CreateShaderResourceViewFromFile() の引数が
 足りないようです。
 実際の定義では D3DX10_IMAGE_LOAD_INFO と ID3DX10ThreadPump* の2つが追加
 されています。

 チュートリアルやプログラミングガイドでも、たまに関数名が間違ってることが
 あるようです。例えば

 DirectX Graphic
  Direct3D 10
   Programming Guide
    API Features
     Reference Counting
 の最後の pDevice->GetRasterrizerState() 等。

 もしかしたら設計の最後に、全部の API 名が RSGetState() のように RS~、PS~、
 OM~、と整理されたのかもしれません。こういうミスはその名残かも。

・その他気がついたこととか随時

HLSL は technique ではなく、technique10。間違えると signature 取れずにはまる。

D3D10 では Set 系で Reference Count が増えない。

Texture の管理に ResourceView を使った場合、開放の手順が多少複雑。
データの実体は ID3D10Texture2D なので、Resource の開放も必要。