少し前から Compute Shader が使えなくなっていました。
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
上のエントリを書いたあと、ドライバを 186.18 に更新したら
CheckFeatureSupport() の D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS で
ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x が 0 を返す
ようになっており、実際に CreateComputeShader() が失敗します。
今回 190.38 に更新してもやっぱり同じで、186.08 に戻したら動きました。
なお CheckFeatureSupport() の D3D11_FEATURE_THREADING で
DriverConcurrentCreates は 1 を返すので、ドライバがきちんと D3D11 に
対応しているのは本当です。
DriverCommandLists が 0 なので Deferred Context はドライバの
エミュレーションになりますが、リソース生成はスレッドに対応している
ことになります。
DriverConcurrentCreates
リソース生成のスレッド対応かどうか。
DriverCommandLists
ドライバが CommandList の記録と実行に直接対応しているかどうか。
0 でもコマンドバッファをソフトウエアで記録するため実行できます。
以前描画周りを Direct3D11 対応で作り直していて積極的にスレッド化したの
ですが、ロード後のリソース生成でまれに失敗することがありました。
よく考えたら当時まだドライバが DriverConcurrentCreates に
対応していなかったので当たり前でした。
関連エントリ
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
上のエントリを書いたあと、ドライバを 186.18 に更新したら
CheckFeatureSupport() の D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS で
ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x が 0 を返す
ようになっており、実際に CreateComputeShader() が失敗します。
今回 190.38 に更新してもやっぱり同じで、186.08 に戻したら動きました。
なお CheckFeatureSupport() の D3D11_FEATURE_THREADING で
DriverConcurrentCreates は 1 を返すので、ドライバがきちんと D3D11 に
対応しているのは本当です。
DriverCommandLists が 0 なので Deferred Context はドライバの
エミュレーションになりますが、リソース生成はスレッドに対応している
ことになります。
DriverConcurrentCreates
リソース生成のスレッド対応かどうか。
DriverCommandLists
ドライバが CommandList の記録と実行に直接対応しているかどうか。
0 でもコマンドバッファをソフトウエアで記録するため実行できます。
以前描画周りを Direct3D11 対応で作り直していて積極的にスレッド化したの
ですが、ロード後のリソース生成でまれに失敗することがありました。
よく考えたら当時まだドライバが DriverConcurrentCreates に
対応していなかったので当たり前でした。
関連エントリ
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
投影されるスクリーンのピクセル長から TessFactor を求めるように変更。
これを曲率で補正します。
前回の頂点形式の tscale はそのまま曲率を意味するようになりました。
この値を無視すれば直線部分も分割可能。

拡大してもフォントが荒れず、同一フォント内でもエッジの長さや奥行きに応じて変わります。
ただし分割数は GS だと限界があります。
GeometryShader の出力は最大 1024 float なので、仮に出力が最小の float4
SV_Position のみとしても 1エッジあたり 21 分割まで。
21 * 3 vertex * 4 edge = 252
252* 4 float = 1008
↑負荷は軽くなったけど TessFactor の求め方はまだまだ改良の余地あり。

↑直線部も均等に割ってランダムな振動を加えたもの。(WARP)
2D のディスプレースメントマップのような感じ。
エッジをじりじり動かせます。手書きアニメーション風の表現とかできるかも。
コンバータにはかなり苦労したけど、実際に画面が出て動くようになるとさまざまな
応用ができて楽しい。
描画が軽いうちは WARP デバイス (D3D_DRIVER_TYPE_WARP) はかなり使えます。
ステートの組み合わせを間違えても GPU がハングアップすることがないので、
いろいろ実験したりテストするとき 精神的 に楽です。
GPU がハングアップすると画面が停止して何もできない状態に陥ります。
たいていはドライバがタイムアウトすると GPU をリセットするので
しばらく待っていれば復活します。
でもうっかりミスだったりするとこの待ち時間が後悔の時間。万が一復活しない場合は
たとえ内部で正常に動いていてもリセットが必要というプレッシャーも。
短時間でも画面のフリーズは無い方がよいです。
関連エントリ
・Direct3D11/DirectX11 (17) GPU を使ったアウトラインフォントの描画の(5)
・Direct3D11/DirectX11 (16) GPU を使ったアウトラインフォントの描画の(4)
・Direct3D11/DirectX11 (15) GPU を使ったアウトラインフォントの描画の(3)
・Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
これを曲率で補正します。
前回の頂点形式の tscale はそのまま曲率を意味するようになりました。
この値を無視すれば直線部分も分割可能。

拡大してもフォントが荒れず、同一フォント内でもエッジの長さや奥行きに応じて変わります。
ただし分割数は GS だと限界があります。
GeometryShader の出力は最大 1024 float なので、仮に出力が最小の float4
SV_Position のみとしても 1エッジあたり 21 分割まで。
21 * 3 vertex * 4 edge = 252
252* 4 float = 1008
↑負荷は軽くなったけど TessFactor の求め方はまだまだ改良の余地あり。

↑直線部も均等に割ってランダムな振動を加えたもの。(WARP)
2D のディスプレースメントマップのような感じ。
エッジをじりじり動かせます。手書きアニメーション風の表現とかできるかも。
コンバータにはかなり苦労したけど、実際に画面が出て動くようになるとさまざまな
応用ができて楽しい。
描画が軽いうちは WARP デバイス (D3D_DRIVER_TYPE_WARP) はかなり使えます。
ステートの組み合わせを間違えても GPU がハングアップすることがないので、
いろいろ実験したりテストするとき 精神的 に楽です。
GPU がハングアップすると画面が停止して何もできない状態に陥ります。
たいていはドライバがタイムアウトすると GPU をリセットするので
しばらく待っていれば復活します。
でもうっかりミスだったりするとこの待ち時間が後悔の時間。万が一復活しない場合は
たとえ内部で正常に動いていてもリセットが必要というプレッシャーも。
短時間でも画面のフリーズは無い方がよいです。
関連エントリ
・Direct3D11/DirectX11 (17) GPU を使ったアウトラインフォントの描画の(5)
・Direct3D11/DirectX11 (16) GPU を使ったアウトラインフォントの描画の(4)
・Direct3D11/DirectX11 (15) GPU を使ったアウトラインフォントの描画の(3)
・Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
2009/08/25
Direct3D Compute Shader 対応の GeForce ドライバ
NVIDIA の新しいドライバ 190.62 がコンピュートシェーダに対応したらしいので試しました。
Windows7 RC/RTM どちらでも Compute Shader 4.0 動いてます。
関連エントリ
・Direct3D11/DirectX11 GeForce の ComputeShader とドライバ
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
Windows7 RC/RTM どちらでも Compute Shader 4.0 動いてます。
(Direct3D11)
*HARDWARE = 10.0 (a000)
feature threading DriverConcurrentCreates=1
feature threading DriverCommandLists=0
feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=1
feature doubles DoublePrecisionFloatShaderOps=0
関連エントリ
・Direct3D11/DirectX11 GeForce の ComputeShader とドライバ
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
やっとフォントデータ丸ごとコンバータを通るようになりました。


表示は Direct3D11 beta + RADEON HD4850
ハードウエアで動かすために GeometryShader 4.0 を使っています。
Reference Driver なら GeometryShader ではなくテセレータを使用可能。
fps 表示もアウトラインフォント

プロポーショナルフォントの処理が無いので字間が少々おかしい。
CPU Rendering の WARP Driver でも 30fps くらいは出ます。
Reference Driver だと 2.4fps 程度。
(WindowsVista x64 Core2 Duo)
頂点形式は float2 x4、これを 4頂点で 1パッチとして描画しています。
4頂点なら LINELIST_ADJ で GeometryShader に渡せます。
1頂点が 1エッジに相当します。tscale はテセレータに対する係数。
係数が 0 だと分割しないので直線部分に使用します。
カーブが急な部分、エッジが長い部分は係数を大きくするなどの調整も可能です。
cx cy はパッチの中心座標を埋め込んだもの。
フォントデータは SVG か FXG を使用しています。glyph や path 等。
パス情報自体はほぼ同じ形式なのでデコード処理は共通化できます。
使用していませんが XAML も attribute に埋め込まれたパスの書式は同一でした。
内外判定は NonZero 相当。自己交差したパスは未対応。
・Paths SVG 1.1
・FXG 1.0 Public Specification
・パス マークアップ構文
関連エントリ
・Direct3D11/DirectX11 (16) GPU を使ったアウトラインフォントの描画の(4)
・Direct3D11/DirectX11 (15) GPU を使ったアウトラインフォントの描画の(3)
・Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作


表示は Direct3D11 beta + RADEON HD4850
ハードウエアで動かすために GeometryShader 4.0 を使っています。
Reference Driver なら GeometryShader ではなくテセレータを使用可能。
fps 表示もアウトラインフォント

プロポーショナルフォントの処理が無いので字間が少々おかしい。
CPU Rendering の WARP Driver でも 30fps くらいは出ます。
Reference Driver だと 2.4fps 程度。
(WindowsVista x64 Core2 Duo)
頂点形式は float2 x4、これを 4頂点で 1パッチとして描画しています。
4頂点なら LINELIST_ADJ で GeometryShader に渡せます。
px py c0x c0y c1x c1y tscale cx px py c0x c0y c1x c1y tscale cy px py c0x c0y c1x c1y tscale 0 px py c0x c0y c1x c1y tscale 0
1頂点が 1エッジに相当します。tscale はテセレータに対する係数。
係数が 0 だと分割しないので直線部分に使用します。
カーブが急な部分、エッジが長い部分は係数を大きくするなどの調整も可能です。
cx cy はパッチの中心座標を埋め込んだもの。
フォントデータは SVG か FXG を使用しています。glyph や path 等。
パス情報自体はほぼ同じ形式なのでデコード処理は共通化できます。
使用していませんが XAML も attribute に埋め込まれたパスの書式は同一でした。
内外判定は NonZero 相当。自己交差したパスは未対応。
・Paths SVG 1.1
・FXG 1.0 Public Specification
・パス マークアップ構文
関連エントリ
・Direct3D11/DirectX11 (16) GPU を使ったアウトラインフォントの描画の(4)
・Direct3D11/DirectX11 (15) GPU を使ったアウトラインフォントの描画の(3)
・Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
2008/11/18
Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・D3D11 の方が対応ハードウエアのカバー範囲が広い。
・D3D10 専用ソフトは動かなくても、D3D11 対応ソフトなら動く可能性がある。
・逆に D3D11 対応ソフトは、D3D10 専用ソフトよりも絵がきれいとは限らない。
シェーダー対応をまとめるとこんな感じでしょうか。
(SM1~5 は ShaderModel1.0~5.0)
Direct3D11 で ShaderModel2.0 が △ なのは、ShaderModel2.0 の固定機能が使えない
可能性があるからです。
例えば 2.0 世代のフォグはハードの固定機能で、RenderState で設定していました。
D3D11 にはこの手のステートがないため同等のブレンドを実現するためにシェーダーに
追加コードが必要です。
その分 Direct3D9 以前を使うよりも速度が落ちる可能性があるからです。
Direct3D10 以降、GeometryShader や HullShader + Tessellator + DomainShader
など、注目すべき新機能がジオメトリに移ってきました。
DirectX8 から始まったシェーダーの拡張は、しばらくはピクセルが中心だったといえます。
固定少数だったピクセルが世代を重ねて完全な 32bit になり、テクスチャルックアップも
制限が無くなり、定数レジスタが増えて実行可能命令数も増加しました。
必要な面積しか演算しなくて済むので効率がよいし、見た目の効果も大きいし、
大量の並列化による演算は CPU には真似ができない分野です。
GPU の力を一番実感しやすかったのかもしれません。
Shader の区別が無くなるところまで行き着いた今、流れはジオメトリに向いています。
単一の描画パイプラインの中で、頂点の生成やプリミティブの削除など自由自在です。
CPU の追い上げもありますが、GPU ならではの活用やさまざまな技法がこれから
生まれてくるものと期待できます。
関連エントリ
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
他
・D3D10 専用ソフトは動かなくても、D3D11 対応ソフトなら動く可能性がある。
・逆に D3D11 対応ソフトは、D3D10 専用ソフトよりも絵がきれいとは限らない。
シェーダー対応をまとめるとこんな感じでしょうか。
FIXED SM1 SM2 SM3 SM4 SM5 DirectX7/D3D7 ○ × × × × × DirectX8/D3D8 ○ ○ × × × × DirectX9/D3D9 ○ ○ ○ ○ × × DirectX/D3D10 × × × × ○ × DirectX/D3D11 × × △ ○ ○ ○
(SM1~5 は ShaderModel1.0~5.0)
Direct3D11 で ShaderModel2.0 が △ なのは、ShaderModel2.0 の固定機能が使えない
可能性があるからです。
例えば 2.0 世代のフォグはハードの固定機能で、RenderState で設定していました。
D3D11 にはこの手のステートがないため同等のブレンドを実現するためにシェーダーに
追加コードが必要です。
その分 Direct3D9 以前を使うよりも速度が落ちる可能性があるからです。
Direct3D10 以降、GeometryShader や HullShader + Tessellator + DomainShader
など、注目すべき新機能がジオメトリに移ってきました。
DirectX8 から始まったシェーダーの拡張は、しばらくはピクセルが中心だったといえます。
固定少数だったピクセルが世代を重ねて完全な 32bit になり、テクスチャルックアップも
制限が無くなり、定数レジスタが増えて実行可能命令数も増加しました。
PixelShader1 固定少数 PixelShader2 浮動小数 16~24bit PixelShader3 浮動小数 32bit、動的分岐 PixelShader4 Unified化
必要な面積しか演算しなくて済むので効率がよいし、見た目の効果も大きいし、
大量の並列化による演算は CPU には真似ができない分野です。
GPU の力を一番実感しやすかったのかもしれません。
Shader の区別が無くなるところまで行き着いた今、流れはジオメトリに向いています。
単一の描画パイプラインの中で、頂点の生成やプリミティブの削除など自由自在です。
CPU の追い上げもありますが、GPU ならではの活用やさまざまな技法がこれから
生まれてくるものと期待できます。
関連エントリ
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
他
Direct3D 11 の Tessellator を使って、GPU だけでアウトラインフォントの描画を
行ってみました。
↓左からそれぞれ TessFactor (分割数) = 1, 2, 12

改良しました。
(1) 事前の変換処理を自動化した。
(2) isoline ではなく quad プリミティブを使ったテセレートを行っており、
必要に応じて任意のエッジをテセレートできる。
まだ (1) がやっとなのであまり最適化していません。
前回
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など

↑左が前回のもの。2点 X-Y を挿入し、赤 (B-C-X-Y) と 緑 (D-E-X-Y) の 2つの
プリミティブに分解。
右が今回のもの。B-C-D-E で 1つの quad で表現され、必要なエッジのみ分割。
中央の X の位置は事前に求めておく
塗りつぶしたフォントの描画のために、事前にプリミティブに分割します。
思ったよりも難しい処理で、特に細長い三角形ができてしまうとアウトラインから
はみ出します。

↑では波線が本来のカーブです。A の上のエッジが沈み込んで対角線に重なっています。
カーブが極端になると対角線エッジの直線部分が表に出てしまいます。
(前回はこれをうまく解決できなかったので手作業だった)
いろいろと試行錯誤した結果がこれ↓です。

↑ましになったもの。
元↓はこちら。

↑だめな分割 (はみ出ているのはバグ)。
(白線=カーブ、青線=直線、ピンクの点=中心の頂点、水色の点=コントロールポイント)
(1) 角度が一番開いた頂点を求める。
(2) (1) の頂点から直線を引ける他の頂点を探す。
(エッジと交差しない、かつ隣接でない、かつ文字の外側ではない)
(3) (2) の条件を満たす頂点の中で一番近い頂点と結ぶ。エッジを挿入して領域を分ける
(4) 4 頂点以下の領域ができたら取り除く。
すべての領域が取り除かれるまで再帰的に繰り返します。
比較的細長い領域への分割を避けてぶつ切りになります。
内外判定のために方向をそろえる必要があるため挿入したエッジは両面分必要です。
循環している可能性があるので、(3) で単一のエッジを挿入しても 2つの領域が
できるとは限りません。
(4) の場合くりぬいた領域が裏返しで繋がっている可能性があるため、4頂点以下の
パスのループでも閉じているかどうか判定が必要です。(対角線の可視など)
上の「だめな分割」の図は失敗しています。
領域毎に、アウトラインをじゃましない位置に中心座標を配置します。(上の図のピンクの点)
ただしアンカーが少なくカーブが極端な場合、最適な中心座標は求まらない可能性が
あります。まだ完全には解決していません。

↑ TessFactor = 8 のワイヤー表示です。
●データの準備
xml で対応ツールが比較的多いことと参照しやすいため、SVG / FXG フォーマットを
使用しています。
・Scalable Vector Graphics (SVG) 1.1 Specification
含まれるカーブの種類は複数あり、直線部分も Line で出力されるので、変換時に
情報を保存しておきます。
現在エッジ情報は 座標ペア × 4 (Cubic Bezier時) で、Arc は未対応です。
v0.xy, c0.xy, c1.xy, v1.xy
Line 時は c0.xy, c1.xy は未使用。c0.x に範囲外の値を入れて、シェーダー内で
未使用(直線)かどうか判断できるようにしています。
(1) カーブの種類をを考慮してパスデータを 4 パラメータに変換
(2) エッジの方向をノーマライズする
(2) は内外判定のため。進行方向 v0 → v1 に向かって右側が必ず文字の外側など、
どちらかに統一されていれば OK。
次回に続きます。
関連エントリ
・Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
行ってみました。
↓左からそれぞれ TessFactor (分割数) = 1, 2, 12

改良しました。
(1) 事前の変換処理を自動化した。
(2) isoline ではなく quad プリミティブを使ったテセレートを行っており、
必要に応じて任意のエッジをテセレートできる。
まだ (1) がやっとなのであまり最適化していません。
前回
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など

↑左が前回のもの。2点 X-Y を挿入し、赤 (B-C-X-Y) と 緑 (D-E-X-Y) の 2つの
プリミティブに分解。
右が今回のもの。B-C-D-E で 1つの quad で表現され、必要なエッジのみ分割。
中央の X の位置は事前に求めておく
塗りつぶしたフォントの描画のために、事前にプリミティブに分割します。
思ったよりも難しい処理で、特に細長い三角形ができてしまうとアウトラインから
はみ出します。

↑では波線が本来のカーブです。A の上のエッジが沈み込んで対角線に重なっています。
カーブが極端になると対角線エッジの直線部分が表に出てしまいます。
(前回はこれをうまく解決できなかったので手作業だった)
いろいろと試行錯誤した結果がこれ↓です。

↑ましになったもの。
元↓はこちら。

↑だめな分割 (はみ出ているのはバグ)。
(白線=カーブ、青線=直線、ピンクの点=中心の頂点、水色の点=コントロールポイント)
(1) 角度が一番開いた頂点を求める。
(2) (1) の頂点から直線を引ける他の頂点を探す。
(エッジと交差しない、かつ隣接でない、かつ文字の外側ではない)
(3) (2) の条件を満たす頂点の中で一番近い頂点と結ぶ。エッジを挿入して領域を分ける
(4) 4 頂点以下の領域ができたら取り除く。
すべての領域が取り除かれるまで再帰的に繰り返します。
比較的細長い領域への分割を避けてぶつ切りになります。
内外判定のために方向をそろえる必要があるため挿入したエッジは両面分必要です。
循環している可能性があるので、(3) で単一のエッジを挿入しても 2つの領域が
できるとは限りません。
(4) の場合くりぬいた領域が裏返しで繋がっている可能性があるため、4頂点以下の
パスのループでも閉じているかどうか判定が必要です。(対角線の可視など)
上の「だめな分割」の図は失敗しています。
領域毎に、アウトラインをじゃましない位置に中心座標を配置します。(上の図のピンクの点)
ただしアンカーが少なくカーブが極端な場合、最適な中心座標は求まらない可能性が
あります。まだ完全には解決していません。

↑ TessFactor = 8 のワイヤー表示です。
●データの準備
xml で対応ツールが比較的多いことと参照しやすいため、SVG / FXG フォーマットを
使用しています。
・Scalable Vector Graphics (SVG) 1.1 Specification
含まれるカーブの種類は複数あり、直線部分も Line で出力されるので、変換時に
情報を保存しておきます。
現在エッジ情報は 座標ペア × 4 (Cubic Bezier時) で、Arc は未対応です。
v0.xy, c0.xy, c1.xy, v1.xy
Line 時は c0.xy, c1.xy は未使用。c0.x に範囲外の値を入れて、シェーダー内で
未使用(直線)かどうか判断できるようにしています。
(1) カーブの種類をを考慮してパスデータを 4 パラメータに変換
(2) エッジの方向をノーマライズする
(2) は内外判定のため。進行方向 v0 → v1 に向かって右側が必ず文字の外側など、
どちらかに統一されていれば OK。
次回に続きます。
関連エントリ
・Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2008/11/24
Direct3D11/DirectX11 (12) テセレータのレンダーステート他
●レンダーステート
テセレータにはレンダーステートがありません。
ID3D11DeviceContext::TSSet~ 等の命令は一切無し。
その代りになるのが Hull Shader のたくさんある attribute です。
HullShader の attribute は単なるヒントではなく、Tessellator の動作そのものを
決定しています。
テセレータの動作を変えるにはシェーダーまるごと置き換える必要があり、レンダー
ステートとシェーダーが一体化しているともいえます。
今までのシェーダーからは少々特殊ですが、管理するオブジェクトが減るので扱いは
楽です。他のシェーダーでも、シェーダーとペアで使うことがわかりきったステートなら
シェーダーに含めてしまって構わないのかもしれません。
●Effect
ShaderModel 5.0 にはまだ Effect ( .fx, fx_5_0 ) が無いようです。
現在のベータ SDK では、core API を使って各シェーダーを個別に生成したり管理する
必要があります。
Effect (ID3D10Effect) を core API 化してしまった Direct3D10 と比べると
何となくトーンダウンしているようにも見えます。
これが方針転換なのか、単に未完成で beta に含まれていないだけなのかまだわかりません。
シェーダー数が増えて大変そうだし後者のような気もします。
だけど Shader Compiler の分離もあるので、D3D10 の反省を踏まえての仕様変更の可能性もあります。
ツールやゲームエンジン等の設計にも関わってくるので、今後どうなるのか気になるところです。
●Tessellator と Triangle patch
Tessellator が扱える patch のタイプには isoline tri quad の 3種類あります。
(Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など)
quad / isoline はこれまでなんどか取り上げたとおり、テセレータは
float2 の DomainLocation (uv) を出力します。
tri の場合は 3次元の uvw を返します。
この u v w はそれぞれ、Triangle の 3頂点に対する割合(距離)を示して
います。(barycentric coordinates)
float3 v1, v2, v3;
pos= v1*uvw.x + v2*uvw.y + v3*uvw.z;
↓左から TessFactor = 222-2、333-3、444-4

中 (InsdeTessFactor) を 2に固定して外側だけ割った場合。
↓左から TessFactor = 111-2、888-2、181-2

右端(181-2)は、1 つのエッジだけ 8分割したものです。
以前アウトラインフォントの描画では quad あたり 1辺の分割に限定して isoline
を使いました。事前処理でこのようなプリミティブにきちんと分解できれば、
一度に複数のエッジを分割対象とすることも可能です。
まだまだ効率化はできるでしょう。
関連エントリ
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
テセレータにはレンダーステートがありません。
ID3D11DeviceContext::TSSet~ 等の命令は一切無し。
その代りになるのが Hull Shader のたくさんある attribute です。
[domain("tri")] [partitioning("fractional_even")] [outputtopology("triangle_ccw")] [outputcontrolpoints(4)] [patchconstantfunc("func")] float4 main( InputPatch<VS_OUTPUT,MAX_POINTS> ip, uint id : SV_OutputControlPointID ) : TSPOS { ... }
HullShader の attribute は単なるヒントではなく、Tessellator の動作そのものを
決定しています。
テセレータの動作を変えるにはシェーダーまるごと置き換える必要があり、レンダー
ステートとシェーダーが一体化しているともいえます。
今までのシェーダーからは少々特殊ですが、管理するオブジェクトが減るので扱いは
楽です。他のシェーダーでも、シェーダーとペアで使うことがわかりきったステートなら
シェーダーに含めてしまって構わないのかもしれません。
●Effect
ShaderModel 5.0 にはまだ Effect ( .fx, fx_5_0 ) が無いようです。
現在のベータ SDK では、core API を使って各シェーダーを個別に生成したり管理する
必要があります。
Effect (ID3D10Effect) を core API 化してしまった Direct3D10 と比べると
何となくトーンダウンしているようにも見えます。
これが方針転換なのか、単に未完成で beta に含まれていないだけなのかまだわかりません。
シェーダー数が増えて大変そうだし後者のような気もします。
だけど Shader Compiler の分離もあるので、D3D10 の反省を踏まえての仕様変更の可能性もあります。
ツールやゲームエンジン等の設計にも関わってくるので、今後どうなるのか気になるところです。
●Tessellator と Triangle patch
Tessellator が扱える patch のタイプには isoline tri quad の 3種類あります。
(Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など)
quad / isoline はこれまでなんどか取り上げたとおり、テセレータは
float2 の DomainLocation (uv) を出力します。
tri の場合は 3次元の uvw を返します。
この u v w はそれぞれ、Triangle の 3頂点に対する割合(距離)を示して
います。(barycentric coordinates)
float3 v1, v2, v3;
pos= v1*uvw.x + v2*uvw.y + v3*uvw.z;
↓左から TessFactor = 222-2、333-3、444-4

中 (InsdeTessFactor) を 2に固定して外側だけ割った場合。
↓左から TessFactor = 111-2、888-2、181-2

右端(181-2)は、1 つのエッジだけ 8分割したものです。
以前アウトラインフォントの描画では quad あたり 1辺の分割に限定して isoline
を使いました。事前処理でこのようなプリミティブにきちんと分解できれば、
一度に複数のエッジを分割対象とすることも可能です。
まだまだ効率化はできるでしょう。
関連エントリ
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2008/09/23
Direct3D11 シェーダーの動的リンクやテクスチャ
●シェーダーの動的リンク
もともと Shader はマテリアルやライティング、アニメーションなど、組み合わせに
応じてすぐに数が増加する傾向があります。
加えて SM2.0~4.0 と徐々に固定機能が廃止され、有効な RenderState が減ると
さらに必要なシェーダー数が増えています。
シェーダー内の分岐もできますが、やはり大規模な並列実行でクリティカルな部分
なので、速度への影響が大きいマテリアルでは慎重になります。
もう1つ心配なのがコンパイル速度です。
シェーダーのコード量が増えるに従って、急激にコンパイルに時間がかかるように
なってきた印象があります。
シェーダーは CPU のコンパイラよりも比較的大胆な最適化が行われているので、
総当たりで比較しているのではないかとたまに思う時があります。
Direct3D11 ではこのような問題を解決するため、動的にシェーダーの Link が
できるようになるとのことです。
SetShader() 時に参照が解決されますが、HW native に変換されたコードで処理が
行われるため、リンクの操作も軽く、実行時の動作も速いそうです。
詳しい仕組みやパフォーマンスへの影響はまだわかりませんが、
分割コンパイルできるし組み合わせによる数の増加が抑えられるので、
管理はかなり便利になると思われます。
逆に、すでにシェーダー管理の仕組みを構築している場合は、D3D10→D3D11 への
移行時にいろいろと修正が必要になるかもしれません。
またいろいろ考えることが増えそうです。
●圧縮テクスチャ BC6/BC7
新しい圧縮テクスチャ BC6 は、HDR テクスチャの圧縮に使われるようです。
16 bpc RGB、つまり R16G16B16 相当を 1/6 = 1byte に圧縮するものと考えられます。
圧縮率 1/6 と書かれていますが、DXGI のフォーマットには 6byte = 48bit
の形式はないため、R16G16B16A16 との対比では alpha が無くなるものの
実質 1/8 のメモリ効率が得られそうです。
圧縮方法は従来の BC1~BC5 (DXT) によく似ています。
4x4 のブロックを単位にエンコードしますが、その手法はより複雑になっているようです。
格納するベースカラーに Alpha が無い代わりに HDR なこと、
ブロック毎にアルゴリズムを柔軟に変更できること、
エリアを分割して複数のパーティションとし、それぞれが異なるカラー補間テーブルを
参照できること、などなど。
これ以上の詳細はわからないので、DirectX11 SDK のリリースを待つ必要があるでしょう。
同じように BC7 は、HDR ではないカラー値の圧縮をより改良したものです。
BC6 同様にブロック毎にアルゴリズムを変更でき、RGB や RGBA の両方に対応できます。
RGB 比 1/3、RGBA で 1/4 とのことなので、やはり 1byte/texel となるようです。
これは BC2~3 (DXT3/DXT5) 等と同じです。
BC7 で完全に置き換えられるなら、BC2/3 は使われなくなるかもしれません。
テクスチャの最大サイズは 16384x16384 まで拡張されるようです。
16384x16384 32bit RGBA 相当で 1GByte
BC1(DXT1)/BC4 で 128MByte
BC2~3(DXT3/5),BC5~7 で 256MByte
関連エントリ
・Direct3D11 マルチスレッドのための機能
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
もともと Shader はマテリアルやライティング、アニメーションなど、組み合わせに
応じてすぐに数が増加する傾向があります。
加えて SM2.0~4.0 と徐々に固定機能が廃止され、有効な RenderState が減ると
さらに必要なシェーダー数が増えています。
シェーダー内の分岐もできますが、やはり大規模な並列実行でクリティカルな部分
なので、速度への影響が大きいマテリアルでは慎重になります。
もう1つ心配なのがコンパイル速度です。
シェーダーのコード量が増えるに従って、急激にコンパイルに時間がかかるように
なってきた印象があります。
シェーダーは CPU のコンパイラよりも比較的大胆な最適化が行われているので、
総当たりで比較しているのではないかとたまに思う時があります。
Direct3D11 ではこのような問題を解決するため、動的にシェーダーの Link が
できるようになるとのことです。
SetShader() 時に参照が解決されますが、HW native に変換されたコードで処理が
行われるため、リンクの操作も軽く、実行時の動作も速いそうです。
詳しい仕組みやパフォーマンスへの影響はまだわかりませんが、
分割コンパイルできるし組み合わせによる数の増加が抑えられるので、
管理はかなり便利になると思われます。
逆に、すでにシェーダー管理の仕組みを構築している場合は、D3D10→D3D11 への
移行時にいろいろと修正が必要になるかもしれません。
またいろいろ考えることが増えそうです。
●圧縮テクスチャ BC6/BC7
新しい圧縮テクスチャ BC6 は、HDR テクスチャの圧縮に使われるようです。
16 bpc RGB、つまり R16G16B16 相当を 1/6 = 1byte に圧縮するものと考えられます。
圧縮率 1/6 と書かれていますが、DXGI のフォーマットには 6byte = 48bit
の形式はないため、R16G16B16A16 との対比では alpha が無くなるものの
実質 1/8 のメモリ効率が得られそうです。
圧縮方法は従来の BC1~BC5 (DXT) によく似ています。
4x4 のブロックを単位にエンコードしますが、その手法はより複雑になっているようです。
格納するベースカラーに Alpha が無い代わりに HDR なこと、
ブロック毎にアルゴリズムを柔軟に変更できること、
エリアを分割して複数のパーティションとし、それぞれが異なるカラー補間テーブルを
参照できること、などなど。
これ以上の詳細はわからないので、DirectX11 SDK のリリースを待つ必要があるでしょう。
同じように BC7 は、HDR ではないカラー値の圧縮をより改良したものです。
BC6 同様にブロック毎にアルゴリズムを変更でき、RGB や RGBA の両方に対応できます。
RGB 比 1/3、RGBA で 1/4 とのことなので、やはり 1byte/texel となるようです。
これは BC2~3 (DXT3/DXT5) 等と同じです。
BC7 で完全に置き換えられるなら、BC2/3 は使われなくなるかもしれません。
テクスチャの最大サイズは 16384x16384 まで拡張されるようです。
16384x16384 32bit RGBA 相当で 1GByte
BC1(DXT1)/BC4 で 128MByte
BC2~3(DXT3/5),BC5~7 で 256MByte
関連エントリ
・Direct3D11 マルチスレッドのための機能
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
2009/06/09
Direct3D11 GeForce driver 186.08 beta
masafumi さんのところで DirectX 11 対応ドライバが
動作したとの報告があがってました。
・新 masafumi's Diary Direct3D11対応ドライバ
CheckFeatureSupport() の結果を調べてみました。
ドライバ 186.08
動いています。threading DriverConcurrentCreates と
d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
が 1 になりました。
以前の結果はこちら。
関連エントリ
・Direct3D10/11 DXGI1.1 とリモート GPU (アダプタ)
動作したとの報告があがってました。
・新 masafumi's Diary Direct3D11対応ドライバ
CheckFeatureSupport() の結果を調べてみました。
ドライバ 186.08
[NVIDIA GeForce 9800 GT ] vmem:497MB sysmem:0MB shmem:2811MB flag=0 (Direct3D11) *HARDWARE = 10.0 (a000) feature threading DriverConcurrentCreates=1 feature threading DriverCommandLists=0 feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=1 feature doubles DoublePrecisionFloatShaderOps=0 [ATI Radeon HD 4670] vmem:504MB sysmem:0MB shmem:2811MB flag=0 (Direct3D11) *HARDWARE = 10.1 (a100) feature threading DriverConcurrentCreates=0 feature threading DriverCommandLists=0 feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0 feature doubles DoublePrecisionFloatShaderOps=0
動いています。threading DriverConcurrentCreates と
d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
が 1 になりました。
以前の結果はこちら。
関連エントリ
・Direct3D10/11 DXGI1.1 とリモート GPU (アダプタ)
2008/11/17
Direct3D11/DirectX11 (10) テセレータの補間
isoline を使うと線分の分割だけでなく 2次元方向にも数を増やすことが出来ます。
おそらく髪の毛の表現などに使われる機能だと考えられます。
uv の u 方向のみライン描画されるので、u が毛の滑らかさ(丸み)、v が密度(本数)
を表します。
出力トポロジの line は domain("isoline") の場合のみ使用できるようです。
前回の記事で tri/quad の出力にも line を含ませていたのは間違いでした。
訂正致します。
テセレータを使うと、かなり簡単に頂点を増やすことができます。
特に isoline は uv 方向に任意に頂点を生成できるため使い勝手が良さそうです。
outputtopology を point にして GeometryShader でオブジェクトを配置すれば、
草むらなどの表現に使えるかもしれません。LOD も容易です。
テセレータは partitioning の指定によってテセレータの各段階を補間できるように
なります。一番シンプルな isoline の 1 line だけで調べてみました。
共通事項として、TessFactor のどれかに 0 が含まれているとプリミティブは表示
されないようです。無関係の固定図形を GeometryShader で描画している場合も
出力されないので、描画が完全に捨てられていると判断できます。
integier の場合、TessFactor が 1.0 の時分割数が 1 です。
isoline で頂点のみ出力すると両端の 2点が表示されます。(点を '*' で表します)
TessFactor= 2.0 の時、線分は 2分割されて 3頂点が表示されます。
同じように各 partitioning タイプ毎に TessFactor を変えて調べてみました。
fractional_odd は奇数の時が基準です。
1.0 で 1分割、3.0 で空間が 3分割されており、その間は補間するように頂点が
移動しています。
fractional_even も同じように偶数時が基準となっています。
2分割と 4分割の中間の図形が生成されています。
integer では中間状態はなく、常に整数値で分割された値になります。
ここで興味深いのは、integer の TessFactor は切り捨てではなく切り上げになる
ことです。1.0 を少しでも超えると 2分割相当となります。
この挙動は他の fractional_even , fractional_odd と見比べると納得できます。
間を補間する場合は、1.0 を少しでも超えると補間のための頂点が生成されて移動を
始めるからです。
今回のテストでは pow2 の動作は integer と同じ結果で違いを発見できませんでした。

上の図は、domain("quad") で partitioning("fractional_odd") の場合の様子を
表示したものです。
それぞれ左上から TessFactor = 1.0、1.5、2.0、3.0 を表しています。
isoline の場合と同じように、quad でも 1.0(1分割) と 3.0(3分割) の間が補間
されていることがわかります。
またその動きも 2次元になっているだけで、基本的には isoline と変わりません。
両端から生成された線分が次の分割状態に近づくよう中心に徐々に移動しています。
テセレータの挙動は想像よりもずっとわかりやすいものでした。テセレータ部だけ
固定機能と言われていたため、もっと決まった動作しかできないかと思ってましたが
HullShader DomainShader のおかげで十分な自由度もあります。
正直かなり使いやすいと思います。
CPU の core 数も増えて、CPU と GPU の違いも少なくなりつつあります。
複雑で特別な仕様の GPU や API をいちいち覚えるくらいなら、必要な機能だけ
CPU で直接書いた方が速いんじゃないかと思ってましたが見直しました。
関連エントリ
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
おそらく髪の毛の表現などに使われる機能だと考えられます。
uv の u 方向のみライン描画されるので、u が毛の滑らかさ(丸み)、v が密度(本数)
を表します。
出力トポロジの line は domain("isoline") の場合のみ使用できるようです。
前回の記事で tri/quad の出力にも line を含ませていたのは間違いでした。
訂正致します。
テセレータを使うと、かなり簡単に頂点を増やすことができます。
特に isoline は uv 方向に任意に頂点を生成できるため使い勝手が良さそうです。
outputtopology を point にして GeometryShader でオブジェクトを配置すれば、
草むらなどの表現に使えるかもしれません。LOD も容易です。
テセレータは partitioning の指定によってテセレータの各段階を補間できるように
なります。一番シンプルな isoline の 1 line だけで調べてみました。
共通事項として、TessFactor のどれかに 0 が含まれているとプリミティブは表示
されないようです。無関係の固定図形を GeometryShader で描画している場合も
出力されないので、描画が完全に捨てられていると判断できます。
integier の場合、TessFactor が 1.0 の時分割数が 1 です。
isoline で頂点のみ出力すると両端の 2点が表示されます。(点を '*' で表します)
1.0= * *
TessFactor= 2.0 の時、線分は 2分割されて 3頂点が表示されます。
2.0= * * *
同じように各 partitioning タイプ毎に TessFactor を変えて調べてみました。
fractional_odd 1.0= * * 1分割 1.5= * * * * 2.0= * * * * 2.5= * * * * 3.0= * * * * 3分割 fractional_even 1.0= * * * 2.0= * * * 2分割 2.5= * *** * 3.0= * * * * * 3.5= * * * * * 4.0= * * * * * 4分割 integer == pow2 1.0= * * 1分割 1.5= * * * 切り上げ 2.0= * * * 2分割 2.5= * * * * 切り上げ 3.0= * * * * 3分割 4.5= * * * * * 切り上げ 4.0= * * * * * 4分割
fractional_odd は奇数の時が基準です。
1.0 で 1分割、3.0 で空間が 3分割されており、その間は補間するように頂点が
移動しています。
fractional_even も同じように偶数時が基準となっています。
2分割と 4分割の中間の図形が生成されています。
integer では中間状態はなく、常に整数値で分割された値になります。
ここで興味深いのは、integer の TessFactor は切り捨てではなく切り上げになる
ことです。1.0 を少しでも超えると 2分割相当となります。
この挙動は他の fractional_even , fractional_odd と見比べると納得できます。
間を補間する場合は、1.0 を少しでも超えると補間のための頂点が生成されて移動を
始めるからです。
今回のテストでは pow2 の動作は integer と同じ結果で違いを発見できませんでした。

上の図は、domain("quad") で partitioning("fractional_odd") の場合の様子を
表示したものです。
それぞれ左上から TessFactor = 1.0、1.5、2.0、3.0 を表しています。
isoline の場合と同じように、quad でも 1.0(1分割) と 3.0(3分割) の間が補間
されていることがわかります。
またその動きも 2次元になっているだけで、基本的には isoline と変わりません。
両端から生成された線分が次の分割状態に近づくよう中心に徐々に移動しています。
テセレータの挙動は想像よりもずっとわかりやすいものでした。テセレータ部だけ
固定機能と言われていたため、もっと決まった動作しかできないかと思ってましたが
HullShader DomainShader のおかげで十分な自由度もあります。
正直かなり使いやすいと思います。
CPU の core 数も増えて、CPU と GPU の違いも少なくなりつつあります。
複雑で特別な仕様の GPU や API をいちいち覚えるくらいなら、必要な機能だけ
CPU で直接書いた方が速いんじゃないかと思ってましたが見直しました。
関連エントリ
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2008/09/22
Direct3D11 マルチスレッドのための機能
PC もマルチコア CPU が当たり前となり、マルチスレッド化がパフォーマンス向上の
鍵となっています。
Gamefest2008 の D3D11 関連のスライドによると、Direct3D11 ではようやく
本格的にマルチスレッドへの対応が行われるとのことです。
・Gamefest 2008 Presentations
これまでは、原則としてレンダリング用の単一のスレッドがリソースアクセスや
描画を行う必要がありました。
D3D11 では非同期のリソース読み込みや複数スレッドからの描画が行えるようです。
またこれらに対応したため、個別に作成された Display List を使った描画も
実質的にできるようになっています。
ゲーム専用ハードでは当たり前のものですが、PC 側にフィードバックされる形と
なったようです。
概念としては現在の Device Interface (ID3D10Device) が、複数の Interface に
分割されます。
(1) Device
(2) Immediate Context
(3) Deferred Context
リソースの作成は従来通り (1) の Device を通して行います。
Thread 対応となり、複数のスレッドで呼ばれても問題が起こりません。
(2) の Immediate Context は従来の ID3D10Device 等での描画に相当し、
レンダリング用スレッドで命令をすぐに発行できます。
(3) の Deferred Context は他のスレッドで描画命令を使用する場合に用いられます。
描画命令はバッファリングされます。いわば内部的に Display List が作られ、
最終的にレンダリングスレッドで Immediate Context と同じように描画命令を発行
できます。
API によっては DispalyList へ乗らないものもあるので、多少の制限は存在します。
基本的に各種ステートがそれぞれの Context 間で干渉することはないようです。
独立して管理し、Display List の描画を行ったあとにステートをリセットした方が
速いとのこと。
この辺の仕組みが具体的にどのような実装になっているのかまだわかりません。
D3D11 は上位互換性があり、D3D10 用の今の GPU でも動作することになっています。
現在の GPU ドライバはスレッド対応になっていないため、呼ばれた API を
バッファに記録して擬似的に Display List をエミュレートする形になるそうです。
リソースアクセスも、使用できるものの lock の粒度が荒くなるため効率は
それなりに落ちるとのこと。
将来的には Direct3D10 世代の GPU でも、ドライバの更新によってスレッド対応化が
行われてパフォーマンスが改善されるようです。
ちなみに現在の D3D9~10 でも、デバイス作成時のフラグで API 呼び出しを
Multi Thread 対応にすることが出来ました。
これは古いやり方で作られており効率悪いため、指定しない方がよいとのことです。
これまでは、使用する Direct3D SDK を次々と乗り換えてきたのは GPU の新しい
機能を使うためであって、GPU の都合によるものでした。
今回の更新は、今のマルチコア CPU を最大限活用するためにも必要となるかも
しれません。
関連エントリ
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
鍵となっています。
Gamefest2008 の D3D11 関連のスライドによると、Direct3D11 ではようやく
本格的にマルチスレッドへの対応が行われるとのことです。
・Gamefest 2008 Presentations
これまでは、原則としてレンダリング用の単一のスレッドがリソースアクセスや
描画を行う必要がありました。
D3D11 では非同期のリソース読み込みや複数スレッドからの描画が行えるようです。
またこれらに対応したため、個別に作成された Display List を使った描画も
実質的にできるようになっています。
ゲーム専用ハードでは当たり前のものですが、PC 側にフィードバックされる形と
なったようです。
概念としては現在の Device Interface (ID3D10Device) が、複数の Interface に
分割されます。
(1) Device
(2) Immediate Context
(3) Deferred Context
リソースの作成は従来通り (1) の Device を通して行います。
Thread 対応となり、複数のスレッドで呼ばれても問題が起こりません。
(2) の Immediate Context は従来の ID3D10Device 等での描画に相当し、
レンダリング用スレッドで命令をすぐに発行できます。
(3) の Deferred Context は他のスレッドで描画命令を使用する場合に用いられます。
描画命令はバッファリングされます。いわば内部的に Display List が作られ、
最終的にレンダリングスレッドで Immediate Context と同じように描画命令を発行
できます。
API によっては DispalyList へ乗らないものもあるので、多少の制限は存在します。
基本的に各種ステートがそれぞれの Context 間で干渉することはないようです。
独立して管理し、Display List の描画を行ったあとにステートをリセットした方が
速いとのこと。
この辺の仕組みが具体的にどのような実装になっているのかまだわかりません。
D3D11 は上位互換性があり、D3D10 用の今の GPU でも動作することになっています。
現在の GPU ドライバはスレッド対応になっていないため、呼ばれた API を
バッファに記録して擬似的に Display List をエミュレートする形になるそうです。
リソースアクセスも、使用できるものの lock の粒度が荒くなるため効率は
それなりに落ちるとのこと。
将来的には Direct3D10 世代の GPU でも、ドライバの更新によってスレッド対応化が
行われてパフォーマンスが改善されるようです。
ちなみに現在の D3D9~10 でも、デバイス作成時のフラグで API 呼び出しを
Multi Thread 対応にすることが出来ました。
これは古いやり方で作られており効率悪いため、指定しない方がよいとのことです。
これまでは、使用する Direct3D SDK を次々と乗り換えてきたのは GPU の新しい
機能を使うためであって、GPU の都合によるものでした。
今回の更新は、今のマルチコア CPU を最大限活用するためにも必要となるかも
しれません。
関連エントリ
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
●TessFactor と方向の対応
テセレータによる分割数は Hull Shader で指定します。
quad の場合上記 6パラメータで指定します。このとき各 TessFactor とテセレータ
出力の uv 値との対応は下記の通り。
例えば u方向を右向き → 、v方向を上向き ↑ として 2D でレンダリングすると
TessFactor[0][1][2][3] は 左、下、右、上 の順番となります。
InsideTessFactor は下記の通り。
SV_InsideTessFactor[0] uの分割 (u=0~1.0)
SV_InsideTessFactor[1] vの分割 (v=0~1.0)
● Dynamic Shader Linkage の補足
Dynamic Shader Linkage には 2つの大きな変更があります。
(1) シェーダー内の命令の分岐先を外部から切換えられるようになった
(2) HLSL の記述で java 風の class 定義ができるようになった
表面的には (2) の方が大きいように見えますが、本来の目的は動的シェーダーリンク
という名前の通り (1) の方です。
むしろ (1) を実現するために、さまざまな候補の中から (2) の実装方法を選択した
だけだといえるかもしれません。
(詳しくはこちら Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage)
シェーダーによって、ハードウエアのレンダリング動作を自分で記述することが
できるようになりました。
自由度が高まる反面、動作の種類毎に個別の異なるシェーダープログラムを用意
しなければならないという問題も生じました。
例えばライティングアルゴリズムによって数種類。基本的なものだけでも
・環境光源
・スカイライト
・平行光源
・点光源
・スポットライト
等複数存在します。
複数光源を照射する場合、オブジェクトの配置位置によって光源の組み合わせが変わる
かもしれません。例えば屋外だと平行光源が必要ですが屋内だと必要なかったりします。
・屋外用光源シェーダー
太陽平行光源+環境スカイライト+エフェクト点光源
・屋内用光源シェーダー
環境光源+点光源×1~4
など。
マテリアルによってはテクスチャをレイヤーで複数重ねたり、それぞれのブレンド式
を変えるかもしれません。
ノーマルマップやレリーフマップを持つ場合、付随して頂点に必要な情報も増えます。
他にも各レイヤー毎に独立した Texcoord (uv) を持たせるのか、テクスチャによって
は共有するのか、パラメータで生成するのかなど。
ゲームで実際に使うことを考えると、法線マップを圧縮したり、アルファチャンネルの
用途をマテリアルによって使い分けたり、場面によって精度を落としたり復号したり、
半透明やテクスチャアルファによる抜きの扱いでシェーダーを分けたり。
影のレンダリングのありなしや、ライトスペースの求め方の違い、フィルタリングの
種類による変更など。
VertexShader でもアニメーションによって、頂点ウエイトの個数、ビルボード化、
ジオメトリインスタンシングへの対応など種類が必要です。
これらをすべて自由に組み合わせられるような汎用的な描画の仕組みを作ると
シェーダーの数は爆発的に増えます。
もちろんシェーダープログラムも外部の情報へアクセスはできますし、分岐やループ
命令もありました。しかしながらシェーダーの組み合わせをによるバリエーション数を
減らすために用意された機能ではなかったことが問題でした。
マテリアル(描画命令)単位で大きな処理の切換えをしたいだけなのに、コード内で
分岐を行うのは少々大げさでです。
最適化の妨げになるし条件判断コードの実行によるパフォーマンスへの影響もあった
ので、どちらを取るか選択を迫られます。
(シェーダーを切換えるための負荷が減るメリットはあります)
Direct3D 11 (Shader Model 5.0) の Dynamic Shader Linkage の導入は
これらの問題を解決するために設けられたものです。
シェーダーのバリエーションを減らして管理しやすくすることが目的で、プログラム内
の命令ジャンプの飛び先を外部から切換えることが可能になりました。
分岐切り替えの粒度も不必要に細かくなく適切で、レンダリング命令単位(マテリアル単位)
で決められるし、その条件判断もシェーダーの外部で一度だけやればいいわけです。
期待が大きい反面、気になる点もあります。
・動的リンクで切換え可能なコードは予め 1つのシェーダーにコンパイルされている必要がある
・最適化への影響
オブジェクトのインスタンスも単なる Buffer へのマッピングに過ぎず、すべて静的に
決まってなければなりません。
非常に巨大な単一のシェーダープログラムができあがる可能性があります。
分岐先の決定は描画時まで遅延されるため、その分 HLSL コンパイラでできる最適化は
減少します。間接ジャンプはインライン化を阻害しますし、結局サブルーチン分の
消費レジスタも予めアロケートしておかなければならないのではないか、との疑問も
生じます。
シェーダーの単なる分岐と本当に違うのか、効果があるのかどうか、
動的リンクによるパフォーマンスの影響はどの程度なのか、
実ハードが出てからあらためて検証する必要がありそうです。
ちなみに D3D11 付属の HLSL コンパイラでは、class 定義だけなら ShaderModel 4.0
以前をターゲットにしても使えます。あくまで表記上の違いだけなので、できることは
今までと変わりません。
動的な飛び先の切り替え (interface 経由の呼び出し) は ShaderModel 5.0 以降のみ
となります。
関連エントリ
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
テセレータによる分割数は Hull Shader で指定します。
float Edge[4] : SV_TessFactor; float Index[2] : SV_InsideTessFactor;
quad の場合上記 6パラメータで指定します。このとき各 TessFactor とテセレータ
出力の uv 値との対応は下記の通り。
u v SV_TessFactor[0] 0.0 0~1.0 SV_TessFactor[1] 0~1.0 0.0 SV_TessFactor[2] 1.0 0~1.0 SV_TessFactor[3] 0~1.0 1.0
例えば u方向を右向き → 、v方向を上向き ↑ として 2D でレンダリングすると
TessFactor[0][1][2][3] は 左、下、右、上 の順番となります。
(0,1) [3] (1,1) ┌─────┐ │ │ │ │ [0]│ │[2] │ │ │ │ └─────┘ (0,0) [1] (1,0)
InsideTessFactor は下記の通り。
SV_InsideTessFactor[0] uの分割 (u=0~1.0)
SV_InsideTessFactor[1] vの分割 (v=0~1.0)
● Dynamic Shader Linkage の補足
Dynamic Shader Linkage には 2つの大きな変更があります。
(1) シェーダー内の命令の分岐先を外部から切換えられるようになった
(2) HLSL の記述で java 風の class 定義ができるようになった
表面的には (2) の方が大きいように見えますが、本来の目的は動的シェーダーリンク
という名前の通り (1) の方です。
むしろ (1) を実現するために、さまざまな候補の中から (2) の実装方法を選択した
だけだといえるかもしれません。
(詳しくはこちら Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage)
シェーダーによって、ハードウエアのレンダリング動作を自分で記述することが
できるようになりました。
自由度が高まる反面、動作の種類毎に個別の異なるシェーダープログラムを用意
しなければならないという問題も生じました。
例えばライティングアルゴリズムによって数種類。基本的なものだけでも
・環境光源
・スカイライト
・平行光源
・点光源
・スポットライト
等複数存在します。
複数光源を照射する場合、オブジェクトの配置位置によって光源の組み合わせが変わる
かもしれません。例えば屋外だと平行光源が必要ですが屋内だと必要なかったりします。
・屋外用光源シェーダー
太陽平行光源+環境スカイライト+エフェクト点光源
・屋内用光源シェーダー
環境光源+点光源×1~4
など。
マテリアルによってはテクスチャをレイヤーで複数重ねたり、それぞれのブレンド式
を変えるかもしれません。
ノーマルマップやレリーフマップを持つ場合、付随して頂点に必要な情報も増えます。
他にも各レイヤー毎に独立した Texcoord (uv) を持たせるのか、テクスチャによって
は共有するのか、パラメータで生成するのかなど。
ゲームで実際に使うことを考えると、法線マップを圧縮したり、アルファチャンネルの
用途をマテリアルによって使い分けたり、場面によって精度を落としたり復号したり、
半透明やテクスチャアルファによる抜きの扱いでシェーダーを分けたり。
影のレンダリングのありなしや、ライトスペースの求め方の違い、フィルタリングの
種類による変更など。
VertexShader でもアニメーションによって、頂点ウエイトの個数、ビルボード化、
ジオメトリインスタンシングへの対応など種類が必要です。
これらをすべて自由に組み合わせられるような汎用的な描画の仕組みを作ると
シェーダーの数は爆発的に増えます。
もちろんシェーダープログラムも外部の情報へアクセスはできますし、分岐やループ
命令もありました。しかしながらシェーダーの組み合わせをによるバリエーション数を
減らすために用意された機能ではなかったことが問題でした。
マテリアル(描画命令)単位で大きな処理の切換えをしたいだけなのに、コード内で
分岐を行うのは少々大げさでです。
最適化の妨げになるし条件判断コードの実行によるパフォーマンスへの影響もあった
ので、どちらを取るか選択を迫られます。
(シェーダーを切換えるための負荷が減るメリットはあります)
Direct3D 11 (Shader Model 5.0) の Dynamic Shader Linkage の導入は
これらの問題を解決するために設けられたものです。
シェーダーのバリエーションを減らして管理しやすくすることが目的で、プログラム内
の命令ジャンプの飛び先を外部から切換えることが可能になりました。
分岐切り替えの粒度も不必要に細かくなく適切で、レンダリング命令単位(マテリアル単位)
で決められるし、その条件判断もシェーダーの外部で一度だけやればいいわけです。
期待が大きい反面、気になる点もあります。
・動的リンクで切換え可能なコードは予め 1つのシェーダーにコンパイルされている必要がある
・最適化への影響
オブジェクトのインスタンスも単なる Buffer へのマッピングに過ぎず、すべて静的に
決まってなければなりません。
非常に巨大な単一のシェーダープログラムができあがる可能性があります。
分岐先の決定は描画時まで遅延されるため、その分 HLSL コンパイラでできる最適化は
減少します。間接ジャンプはインライン化を阻害しますし、結局サブルーチン分の
消費レジスタも予めアロケートしておかなければならないのではないか、との疑問も
生じます。
シェーダーの単なる分岐と本当に違うのか、効果があるのかどうか、
動的リンクによるパフォーマンスの影響はどの程度なのか、
実ハードが出てからあらためて検証する必要がありそうです。
ちなみに D3D11 付属の HLSL コンパイラでは、class 定義だけなら ShaderModel 4.0
以前をターゲットにしても使えます。あくまで表記上の違いだけなので、できることは
今までと変わりません。
動的な飛び先の切り替え (interface 経由の呼び出し) は ShaderModel 5.0 以降のみ
となります。
関連エントリ
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
DirectX SDK Novemver 2008 には DirectX11 (D3D11) の beta SDK が含まれています。
Direct3D11 API の機能や詳細が明らかになってきました。
Direct3D10 で大幅に変更された API の路線は継承されていますが
さらに現実的な改良がいくつも施されています。
中でも驚いたのが、D3D10 の時よりも間口を広げたこと。
手持ちの RADEON HD4850 でも D3D11 サンプルが動作しました。
(Windows Vista x64 + HD4850 CATALYST 8.10)
サンプルによっては、Reference でなくアクセラレートが利いているものもあります。
D3D9 GPU では一切動かなかった D3D10 とはえらい違いです。
信じられないくらい。
■D3D9 → D3D10 の場合
・Preview SDK が出ても当時は Vista 発売前だったため Reference Driver ですら
動かなかった。
・D3D10 専用 GPU (ShaderModel 4.0 以上) が無いと一切ハードウエアアクセラレータ
が利かなかった。(Reference のみ)
■D3D10 → D3D11 の場合
・Vista 上なら旧ビデオカードでも Reference Device で動く。
・D3D10 GPU でも D3D11 API が使えた。Feature Level が制限されるだけで API
自体は置き換えられる。未対応の機能さえ使わなければアクセラレートされるらしい。
・さらに WARP Driver (WARP Device) が使える。(詳細は後述)
D3D9 以前は CAPS 情報があったため、旧 GPU でも使えない機能が判別できました。
D3D10 はデバイスの性能を特定する仕組みが無く、D3D10 の全機能が必須となり
旧 GPU は切り捨てられました。
D3D10.1 では FeatureLevel が導入され、10.0 と同居可能となりました。
D3D11 では同じように FeatureLevel で各 GPU のレベルを判別します。
上記 FeatureLevel の定義を見ると、D3D10 GPU どころか D3D9 世代、
ShaderModel 2.0~3.0 にも対応しているように見えます。
もしこれが本当なら、Direct3D10 よりも Direct3D11 の方が対応 GPU の範囲が広い
ことになります。
Direct3D10 は動かないけど Direct3D11 なら動く可能性があるということ。
Direct3D9 の固定パイプライン(HW T&L) が使えないだけで、従来の D3D9Ex も
D3D11 API に統合可能なのかもしれません。
互換性を保った API の置き換えは D3D9 以前の DirectX では当たり前だったため、
元に戻っただけともいえます。
それでも D3D10 の仕様を考えると非常に大きな路線変更です。
Direct3D10 へ移行するのはものすごい骨が折れるし敷居も高かったので良い傾向です。
FeatureLevel 9.2 と 9.1 の違いは不明です。
ShaderModel は vs3.0/vs2.0 ではなく、vs_4_0_level_9_1 等 4.0 の制限版と
なるようです。
●Device Type (Driver Type)
Device Type が増えました。
目新しいのは Null Reference Device と WARP Device です。
・Hardware Device (HAL)
・Null Reference Device
・Reference Device
・Software Device
・WARP Device
Null Reference Device は Reference Device 同様ソフトウエア動作しますが
レンダリング機能が無いようです。API 呼び出しだけソフトウエアでシミュレート
することで、現実的な速度でデバッグを行うのが目的のようです。
WARP Device (Windows Advanced Rasterizer) は高速なソフトウエアラスタライザ
のようです。Multi Core CPU が当たり前になり GPU との差が埋まりつつあるため、
ソフトウエアラスタライザが復活といったところでしょうか。
WARP Device は D3D11/D3D10.1 の置き換えが出来るそうです。
詳しい説明がないため中身はわかりませんが、RAMP とか MMX RGB Driver が存在
していた D3D5~D3D6 あたりを思い出します。
実際に D3D_DRIVER_TYPE_WARP を使ってみたところ、D3D10WARP_beta.dll が
読み込まれているのを確認しました。
Dual Core CPU で試したためあまり参考にならないかもしれませんが、確かに動作は
REFERENCE Driver より高速でした。
Reference で 0.3fps 程度のサンプルが、数fps 程度には向上しています。
Quad 以上ならもっと効果が顕著だと思われます。
これから CPU の core 数は増加していきます。例えば Core i7 は Quad + HT で
ハードウエア 8 スレッドらしいので、今後はさらに期待できそうです。
DirectX11 に関しては下記関連エントリも参考にしてください。
関連エントリ
・Direct3D11 シェーダーの動的リンクやテクスチャ
・Direct3D11 マルチスレッドのための機能
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
Direct3D11 API の機能や詳細が明らかになってきました。
Direct3D10 で大幅に変更された API の路線は継承されていますが
さらに現実的な改良がいくつも施されています。
中でも驚いたのが、D3D10 の時よりも間口を広げたこと。
手持ちの RADEON HD4850 でも D3D11 サンプルが動作しました。
(Windows Vista x64 + HD4850 CATALYST 8.10)
サンプルによっては、Reference でなくアクセラレートが利いているものもあります。
D3D9 GPU では一切動かなかった D3D10 とはえらい違いです。
信じられないくらい。
■D3D9 → D3D10 の場合
・Preview SDK が出ても当時は Vista 発売前だったため Reference Driver ですら
動かなかった。
・D3D10 専用 GPU (ShaderModel 4.0 以上) が無いと一切ハードウエアアクセラレータ
が利かなかった。(Reference のみ)
■D3D10 → D3D11 の場合
・Vista 上なら旧ビデオカードでも Reference Device で動く。
・D3D10 GPU でも D3D11 API が使えた。Feature Level が制限されるだけで API
自体は置き換えられる。未対応の機能さえ使わなければアクセラレートされるらしい。
・さらに WARP Driver (WARP Device) が使える。(詳細は後述)
D3D9 以前は CAPS 情報があったため、旧 GPU でも使えない機能が判別できました。
D3D10 はデバイスの性能を特定する仕組みが無く、D3D10 の全機能が必須となり
旧 GPU は切り捨てられました。
D3D10.1 では FeatureLevel が導入され、10.0 と同居可能となりました。
D3D11 では同じように FeatureLevel で各 GPU のレベルを判別します。
Feature Level 11.0 = ShaderModel 5.0 10.1 = ShaderModel 4.1 10.0 = ShaderModel 4.0 9.3 = ShaderModel 3.0 9.2 = ShaderModel 2.0 9.1 = ShaderModel 2.0
上記 FeatureLevel の定義を見ると、D3D10 GPU どころか D3D9 世代、
ShaderModel 2.0~3.0 にも対応しているように見えます。
もしこれが本当なら、Direct3D10 よりも Direct3D11 の方が対応 GPU の範囲が広い
ことになります。
Direct3D10 は動かないけど Direct3D11 なら動く可能性があるということ。
Direct3D9 の固定パイプライン(HW T&L) が使えないだけで、従来の D3D9Ex も
D3D11 API に統合可能なのかもしれません。
互換性を保った API の置き換えは D3D9 以前の DirectX では当たり前だったため、
元に戻っただけともいえます。
それでも D3D10 の仕様を考えると非常に大きな路線変更です。
Direct3D10 へ移行するのはものすごい骨が折れるし敷居も高かったので良い傾向です。
FeatureLevel 9.2 と 9.1 の違いは不明です。
ShaderModel は vs3.0/vs2.0 ではなく、vs_4_0_level_9_1 等 4.0 の制限版と
なるようです。
●Device Type (Driver Type)
Device Type が増えました。
目新しいのは Null Reference Device と WARP Device です。
・Hardware Device (HAL)
・Null Reference Device
・Reference Device
・Software Device
・WARP Device
Null Reference Device は Reference Device 同様ソフトウエア動作しますが
レンダリング機能が無いようです。API 呼び出しだけソフトウエアでシミュレート
することで、現実的な速度でデバッグを行うのが目的のようです。
WARP Device (Windows Advanced Rasterizer) は高速なソフトウエアラスタライザ
のようです。Multi Core CPU が当たり前になり GPU との差が埋まりつつあるため、
ソフトウエアラスタライザが復活といったところでしょうか。
WARP Device は D3D11/D3D10.1 の置き換えが出来るそうです。
詳しい説明がないため中身はわかりませんが、RAMP とか MMX RGB Driver が存在
していた D3D5~D3D6 あたりを思い出します。
実際に D3D_DRIVER_TYPE_WARP を使ってみたところ、D3D10WARP_beta.dll が
読み込まれているのを確認しました。
Dual Core CPU で試したためあまり参考にならないかもしれませんが、確かに動作は
REFERENCE Driver より高速でした。
Reference で 0.3fps 程度のサンプルが、数fps 程度には向上しています。
Quad 以上ならもっと効果が顕著だと思われます。
これから CPU の core 数は増加していきます。例えば Core i7 は Quad + HT で
ハードウエア 8 スレッドらしいので、今後はさらに期待できそうです。
DirectX11 に関しては下記関連エントリも参考にしてください。
関連エントリ
・Direct3D11 シェーダーの動的リンクやテクスチャ
・Direct3D11 マルチスレッドのための機能
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
結局 GeometryShader だけで描画できました。
これだと D3D11 でもハードウエアアクセラレートかかるので
回転アニメーションとかも余裕。

入力頂点は、最後の1つを複製して 6頂点単位に変更。
それぞれが float2 × 4 なのは変わらず。
・エッジ x4
・中心座標
・ダミー
描画 TOPOLOGY は D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ に変更。
これを triangleadj で受け取れば 6 頂点入力ができます。
HS/DS を NULL にして代わりに GS を設定するだけ。
各エッジを最大 8 分割と仮定すると最大
8 triangle × 3 × 4edge = 96 頂点出力される可能性があります。
各シェーダーを vs_4_0 gs_4_0 ps_4_0 向けにコンパイルすると
RADEON HD4850 で HARDWARE 動作しました。API は D3D11 のまま。
最適化していないので、四隅と中心の頂点の計算が重複したままです。
関連エントリ
・Direct3D11/DirectX11 (15) GPU を使ったアウトラインフォントの描画の(3)
・Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
・Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
これだと D3D11 でもハードウエアアクセラレートかかるので
回転アニメーションとかも余裕。

入力頂点は、最後の1つを複製して 6頂点単位に変更。
それぞれが float2 × 4 なのは変わらず。
・エッジ x4
・中心座標
・ダミー
描画 TOPOLOGY は D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ に変更。
これを triangleadj で受け取れば 6 頂点入力ができます。
HS/DS を NULL にして代わりに GS を設定するだけ。
iContext->RSSetState( iRS_WIREFRAME ); UINT stride= sizeof(float)*2*4; UINT offset= 0; iContext->IASetVertexBuffers( 0, 1, &iVBuffer, &stride, &offset ); iContext->IASetInputLayout( iLayout ); iContext->VSSetShader( iVS, NULL, 0 ); iContext->GSSetConstantBuffers( 0, 1, &iBufferV ); iContext->GSSetShader( iGS, NULL, 0 ); iContext->PSSetShader( iPS, NULL, 0 ); iContext->OMSetRenderTargets( 1, &iRenderTargetView, iDepthStencilView ); iContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ ); iContext->Draw( vdata0_size*sizeof(float)/(sizeof(float)*2*4), 0 );
各エッジを最大 8 分割と仮定すると最大
8 triangle × 3 × 4edge = 96 頂点出力される可能性があります。
各シェーダーを vs_4_0 gs_4_0 ps_4_0 向けにコンパイルすると
RADEON HD4850 で HARDWARE 動作しました。API は D3D11 のまま。
// gs_4_0 struct VS_OUTPUT { float2 vPos0 : VSPOS0; float2 vC0 : VSPOS1; float2 vC1 : VSPOS2; float2 vPos1 : VSPOS3; }; cbuffer pf { float4x4 WVP; }; struct DS_OUTPUT { float4 vPos : SV_Position; }; void addv( float2 pos, inout TriangleStream<DS_OUTPUT> stream ) { DS_OUTPUT dout; dout.vPos= mul( float4( pos.xy, 0, 1 ), WVP ); stream.Append( dout ); } float2 UVtoPositionB( const VS_OUTPUT p, float t ) { float t2= 1.0-t; float4 u= float4( t2*t2*t2, t2*t2* t*3, t2* t* t*3, t* t* t ); return u.x*p.vPos0.xy +u.y*p.vC0.xy +u.z*p.vC1.xy +u.w*p.vPos1.xy; } void Curve4( float2 center, const VS_OUTPUT v, inout TriangleStream<DS_OUTPUT> stream ) { const int step= 8; // = TessFactor float t= 0; float tstep= 1.0/step; float2 prevpos= UVtoPositionB( v, 0 ); for( int i= 0 ; i< step ; i++ ){ t+= tstep; float2 pos= UVtoPositionB( v, t ); addv( prevpos, stream ); addv( pos, stream ); addv( center, stream ); stream.RestartStrip(); prevpos= pos; } } void Edge( float2 center, const VS_OUTPUT v, inout TriangleStream<DS_OUTPUT> stream ) { if( v.vC0.x > -1e4 ){ Curve4( center, v, stream ); }else{ // 直線の場合 addv( v.vPos0, stream ); addv( v.vPos1, stream ); addv( center, stream ); stream.RestartStrip(); } } // step(8) x 3 x 4 = 96 [maxvertexcount(96)] void main( triangleadj VS_OUTPUT input[6], inout TriangleStream<DS_OUTPUT> stream ) { Edge( input[4].vPos0, input[0], stream ); Edge( input[4].vPos0, input[1], stream ); Edge( input[4].vPos0, input[2], stream ); Edge( input[4].vPos0, input[3], stream ); }
最適化していないので、四隅と中心の頂点の計算が重複したままです。
関連エントリ
・Direct3D11/DirectX11 (15) GPU を使ったアウトラインフォントの描画の(3)
・Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
・Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
2009/05/13
Direct3D10/11 DXGI1.1 とリモート GPU (アダプタ)
DXGI1.1 のアダプタ列挙を試します。
●EeePC901-X で実行した場合 (Windows7 RC x86)
945 内蔵 GPU が列挙できています。
実際に Direct3D10 / 11 デバイスを作るとエラーなので、ドライバがまだ D3D10 Level9
に対応していないことがわかります。
●デスクトップ PC で実行した場合 (Windows7 RC x64)
2枚差したビデオカードが両方とも列挙されています。
それぞれ 10.1 でデバイスの作成ができます。
●リモートデスクトップ経由で実行した場合
EeePC 901 からデスクトップ PC に接続しています。
予想通り両方のマシン、すべてのアダプタが見えていることがわかります。
EeePC の 945 では DXGI_ADAPTER_DESC1 の Flags に DXGI_ADAPTER_FLAG_REMOTE bit が立っており、このデバイスを区別することが可能です。
プログラム自体はホスト側のデスクトップ PC で走っているので、プログラムから見れば
クライアントである EeePC の方がリモートです。
もし Remote のアダプタが Command Remoting に対応していれば
ネットワークを経由していてもクライアント側の PC でリアルタイムに
レンダリングすることが可能となります。
ちなみに DXGI1.0 で列挙するとホスト側の最初のアダプタ "RADEON HD4850" しか
見えませんでした。
● Feature
各アダプタの詳細を調べると下記の通り。
いわば Direct3D11 における caps です。
以前 DirectX SDK Nov2008 時点で調べた結果はこちら。
ほとんどのオプションは 0 のままですが、よく見ると REFERENCE で
DoublePrecisionFloatShaderOps が 1 になっていました。
REFERENCE を使えばシェーダーの double 演算命令もテストできるようになったということです。
同記事 でも触れていますが ComputeShader 4.0/4.1 については November の時点ですでに対応していますね。
関連エントリ
・Direct3D DXGI とマルチ GPU (アダプタ)
・Windows7 リモートデスクトップと Direct3D
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
●EeePC901-X で実行した場合 (Windows7 RC x86)
// EeePC 901-X [Mobile Intel(R) 945 Express Chipset Family (Microsoft Corporation - WDDM 1.0)] vmem:0MB sysmem:64MB shmem:192MB flag=0
945 内蔵 GPU が列挙できています。
実際に Direct3D10 / 11 デバイスを作るとエラーなので、ドライバがまだ D3D10 Level9
に対応していないことがわかります。
●デスクトップ PC で実行した場合 (Windows7 RC x64)
// Desktop PC [ATI Radeon HD 4800 Series ] vmem:504MB sysmem:0MB shmem:2811MB flag=0 (Direct3D11) *HARDWARE = 10.1 (a100) [ATI Radeon HD 4670] vmem:504MB sysmem:0MB shmem:2811MB flag=0 (Direct3D11) *HARDWARE = 10.1 (a100)
2枚差したビデオカードが両方とも列挙されています。
それぞれ 10.1 でデバイスの作成ができます。
●リモートデスクトップ経由で実行した場合
[Mobile Intel(R) 945 Express Chipset Family (Microsoft Corporation - WDDM 1.0)] vmem:0MB sysmem:64MB shmem:192MB flag=1 REMOTE [ATI Radeon HD 4800 Series ] vmem:504MB sysmem:0MB shmem:2811MB flag=0 (Direct3D11) *HARDWARE = 10.1 (a100) [ATI Radeon HD 4670] vmem:504MB sysmem:0MB shmem:2811MB flag=0 (Direct3D11) *HARDWARE = 10.1 (a100)
EeePC 901 からデスクトップ PC に接続しています。
予想通り両方のマシン、すべてのアダプタが見えていることがわかります。
EeePC の 945 では DXGI_ADAPTER_DESC1 の Flags に DXGI_ADAPTER_FLAG_REMOTE bit が立っており、このデバイスを区別することが可能です。
プログラム自体はホスト側のデスクトップ PC で走っているので、プログラムから見れば
クライアントである EeePC の方がリモートです。
もし Remote のアダプタが Command Remoting に対応していれば
ネットワークを経由していてもクライアント側の PC でリアルタイムに
レンダリングすることが可能となります。
ちなみに DXGI1.0 で列挙するとホスト側の最初のアダプタ "RADEON HD4850" しか
見えませんでした。
● Feature
各アダプタの詳細を調べると下記の通り。
いわば Direct3D11 における caps です。
[DEFAULT] (Direct3D11) *HARDWARE = 10.1 (a100) feature threading DriverConcurrentCreates=0 feature threading DriverCommandLists=0 feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0 feature doubles DoublePrecisionFloatShaderOps=0 *REFERENCE = 11.0 (b000) feature threading DriverConcurrentCreates=0 feature threading DriverCommandLists=0 feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=1 feature doubles DoublePrecisionFloatShaderOps=1 *WARP = 10.1 (a100) feature threading DriverConcurrentCreates=0 feature threading DriverCommandLists=0 feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0 feature doubles DoublePrecisionFloatShaderOps=0 [ATI Radeon HD 4800 Series ] (V:4098 D:37954 S:84021250 R:0) luid:000097b2 00000000 vmem:504MB sysmem:0MB shmem:2811MB flag=0 (Direct3D11) *HARDWARE = 10.1 (a100) feature threading DriverConcurrentCreates=0 feature threading DriverCommandLists=0 feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0 feature doubles DoublePrecisionFloatShaderOps=0 [ATI Radeon HD 4670] (V:4098 D:38032 S:625086466 R:0) luid:0000b18e 00000000 vmem:504MB sysmem:0MB shmem:2811MB flag=0 (Direct3D11) *HARDWARE = 10.1 (a100) feature threading DriverConcurrentCreates=0 feature threading DriverCommandLists=0 feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0 feature doubles DoublePrecisionFloatShaderOps=0
以前 DirectX SDK Nov2008 時点で調べた結果はこちら。
ほとんどのオプションは 0 のままですが、よく見ると REFERENCE で
DoublePrecisionFloatShaderOps が 1 になっていました。
REFERENCE を使えばシェーダーの double 演算命令もテストできるようになったということです。
同記事 でも触れていますが ComputeShader 4.0/4.1 については November の時点ですでに対応していますね。
関連エントリ
・Direct3D DXGI とマルチ GPU (アダプタ)
・Windows7 リモートデスクトップと Direct3D
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
2013/12/23
Android OpenGL ES 2.0 と 3.0 の切り替え
Desktop の OpenGL 4.x との互換性を考えるならば、
OpenGL ES 3.0 の方が圧倒的に相性が良いです。
DirectX で例えるなら Direct3D11/10 と Direct3D9 を比べるようなもの。
Direct3D11 (OpenGL4) と Direct3D10 (OpenGL ES 3.0) は同じ API 構造を
保っていますが Direct3D9 (OpenGL ES 2.0) は大きく異なります。
OpenGL は DirectX ほどの違いはなく API 上は上位互換性を保っています。
ですが Shader の構文や描画パイプラインの設計は、やはりどちらかに
合わせる必要があります。
設計が大きく異なるならば、OpenGL ES 2.0 と OpenGL ES 3.0 のバイナリを
完全に分けてしまった方が良いのかもしれません。
以前 NDK の dll を分離する方法を紹介しました。
・Android NDK の初期化と dll の分離
もし dll を分割して 2段階で読み込んでいるなら、同じ方法で
バイナリを切り替えることができます。
例えば下記のように。
・libjniproxy.so
・libappmain_es2.so
・libappmain_es3.so
関連エントリ
・Android OpenGL ES 3.0 対応 GPU を判定する方法
・Android 4.4 Adreno 320 の OpenGL ES 3.0 と Version 判定
・Android NDK の初期化と dll の分離
OpenGL ES 3.0 の方が圧倒的に相性が良いです。
DirectX で例えるなら Direct3D11/10 と Direct3D9 を比べるようなもの。
Direct3D11 (OpenGL4) と Direct3D10 (OpenGL ES 3.0) は同じ API 構造を
保っていますが Direct3D9 (OpenGL ES 2.0) は大きく異なります。
OpenGL は DirectX ほどの違いはなく API 上は上位互換性を保っています。
ですが Shader の構文や描画パイプラインの設計は、やはりどちらかに
合わせる必要があります。
設計が大きく異なるならば、OpenGL ES 2.0 と OpenGL ES 3.0 のバイナリを
完全に分けてしまった方が良いのかもしれません。
以前 NDK の dll を分離する方法を紹介しました。
・Android NDK の初期化と dll の分離
もし dll を分割して 2段階で読み込んでいるなら、同じ方法で
バイナリを切り替えることができます。
例えば下記のように。
・libjniproxy.so
・libappmain_es2.so
・libappmain_es3.so
関連エントリ
・Android OpenGL ES 3.0 対応 GPU を判定する方法
・Android 4.4 Adreno 320 の OpenGL ES 3.0 と Version 判定
・Android NDK の初期化と dll の分離
2009/08/01
OpenGLES2.0 シェーダー管理
OpenGL 系はほとんど使っていないので、余りよくわかっていないことが多いです。
とりあえずシェーダーのバリエーション管理をどうするか。
Direct3D11 では D3DCompile() 命令にプリプロセッサ用のデータを渡すことが
できます。シェーダーコンパイラは Direct3D11 より中立化しました。
D3DCompiler.h / d3dcompiler.lib と独立したモジュールになっており、
FeatureLevel や D3D のナンバーと関係なく共通で使用します。
include に対応するためには ID3DInclude を定義します。
define シンボルを渡す場合は、D3D_SHADER_MACRO 構造体の配列を作っておきます。
(Direct3D 10 Shader4.0 シェーダーのコンパイル)
Shader の機能わけのために、これまでプリプロセッサ命令を活用してきました。
同じシェーダープログラムでもシンボル定義に応じて複数の機能に対応させます。
例えば上記の配列を D3DCompiler() に渡すと、hlsl 上では SF_ANIMATION の定義が
"1" になります。
シェーダー (hlsl) 上では
といった感じでコンパイル時の機能指定に応じてコードを分けることができます。
OpenGLES の GLSL コンパイラの場合、特にプリプロセッサ用の定義データを
そのまま渡すわけではなさそうです。
glShaderSource() では、一度のコンパイルでも複数のソースを同時に渡すことができます。
このうち 1つをプログラム内で生成したテキストに割り当てます。
例えばシェーダーコンパイル時に
といったテキストを生成してソースの 1つとして与えることで、D3D_SHADER_MACRO
と同じような使い方ができました。
OpenGLES2.0 はシェーダーのみと割り切ったおかげで無駄のないすっきりした
構成になっているようです。
シェーダーの世代的には ShaderModel3.0 相当と思われます。
Direct3D9 + ShaderModel3.0 の場合も、多くの機能がシェーダーで汎用化された
ために従来固定機能だったパイプラインの機能がいくつか使えなくなっています。
それでも API 的には重複する固定機能の設定が残っていたため、どちらでも動くのか
どうか、使えるのかどうかなどわかりにくい点がありました。
この問題は Direct3D10 でいったんリセットすることで解消しています。
Direct3D 10 以降はシェーダーのみと割り切って、重複する固定機能の多くを
排除しました。
そういう意味では OpenGLES2.0 は、Direct3D9 + ShaderModel3.0 というよりも、
Direct3D10 + ShaderModel4_0_Level_9_3 の方により近いといえるかもしれません。
関連エントリ
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D 10 Shader4.0 シェーダーのコンパイル
とりあえずシェーダーのバリエーション管理をどうするか。
Direct3D11 では D3DCompile() 命令にプリプロセッサ用のデータを渡すことが
できます。シェーダーコンパイラは Direct3D11 より中立化しました。
D3DCompiler.h / d3dcompiler.lib と独立したモジュールになっており、
FeatureLevel や D3D のナンバーと関係なく共通で使用します。
include に対応するためには ID3DInclude を定義します。
define シンボルを渡す場合は、D3D_SHADER_MACRO 構造体の配列を作っておきます。
(Direct3D 10 Shader4.0 シェーダーのコンパイル)
Shader の機能わけのために、これまでプリプロセッサ命令を活用してきました。
同じシェーダープログラムでもシンボル定義に応じて複数の機能に対応させます。
D3D_SHADER_MACRO deflist[]= { { "SF_ANIMATION", "1", }, { NULL, NULL }, };
例えば上記の配列を D3DCompiler() に渡すと、hlsl 上では SF_ANIMATION の定義が
"1" になります。
シェーダー (hlsl) 上では
#if SF_ANIMATION ~ #endif
といった感じでコンパイル時の機能指定に応じてコードを分けることができます。
OpenGLES の GLSL コンパイラの場合、特にプリプロセッサ用の定義データを
そのまま渡すわけではなさそうです。
glShaderSource() では、一度のコンパイルでも複数のソースを同時に渡すことができます。
このうち 1つをプログラム内で生成したテキストに割り当てます。
例えばシェーダーコンパイル時に
#define SF_ANIMATION 1
といったテキストを生成してソースの 1つとして与えることで、D3D_SHADER_MACRO
と同じような使い方ができました。
OpenGLES2.0 はシェーダーのみと割り切ったおかげで無駄のないすっきりした
構成になっているようです。
シェーダーの世代的には ShaderModel3.0 相当と思われます。
Direct3D9 + ShaderModel3.0 の場合も、多くの機能がシェーダーで汎用化された
ために従来固定機能だったパイプラインの機能がいくつか使えなくなっています。
それでも API 的には重複する固定機能の設定が残っていたため、どちらでも動くのか
どうか、使えるのかどうかなどわかりにくい点がありました。
この問題は Direct3D10 でいったんリセットすることで解消しています。
Direct3D 10 以降はシェーダーのみと割り切って、重複する固定機能の多くを
排除しました。
そういう意味では OpenGLES2.0 は、Direct3D9 + ShaderModel3.0 というよりも、
Direct3D10 + ShaderModel4_0_Level_9_3 の方により近いといえるかもしれません。
関連エントリ
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D 10 Shader4.0 シェーダーのコンパイル
2008/09/21
Direct3D11 テセレータとシェーダー
細かい使い方まではわかりませんが、様々な用例から構造が少しずつ見えてきています。
プログラマブルなシェーダー Hull Shader と Domain Shader に挟まれた形で
Tessellator が入ります。
このテセレータというのは、位置的にも機能的にもラスタライザに非常に近いもの
なのかもしれません。
そう思えば固定機能であることも納得できます。
DomainShader はラスタライズ後に走る Pixsel Shader に相当し、規模も走る
スレッド数も動的に変化します。
テクスチャによってエフェクトが入る点も似ています。
DomainShader は Displacement Map を読み込むので、Pixsel Shader に次いで
テクスチャアクセスを多用するシェーダーとなりそうです。
そもそも、動的にサイズが変化するユニットだからこそイメージマップを利用する
意味があるわけで、必然というか当たり前のことなのかもしれません。
単一のパイプライン中に Tessellator と Rasterizer の 2ヶ所でデータの増幅が
行われます。本当は Geometry Shader でも可能なので 3ヶ所です。
逆にポリゴン減らすのは、生成後のプリミティブ単位で実行される Geometry Shader
でしか出来ないと思われます。
もしマップによってメッシュに穴を開けたいなら、GeometryShader でもテクスチャを
サンプリングするか、Domain Shader からパラメータを渡すことになるのでしょう。
ピクセルを減らすこと自体は PiselShader でも clip (discard) で可能です。
Vertex 単位で走る VertexShader や、Pixel 単位で走る PixelShader を考えると、
GeometryShader は PrimitiveShader といった方が理解しやすいかもしれません。
となると HullShader は PatchShader です。
DomainShader は Post Vertex Shader といった位置づけで、Tessellation 後の
VertexShader に相当します。
本来の VertexShader は Pre Vertex Shader。
だいぶすっきりしました。
でも全部略称が PS になって区別できないからやっぱりだめです。
Tessellator や Displacement Map によって 3D データの作り方は今後変わっていく
と思われます。
3D だけでなく、ベクタフォントを GPU で直接描画するなど 2D 面でも応用できる
かもしれません。
関連エントリ
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
プログラマブルなシェーダー Hull Shader と Domain Shader に挟まれた形で
Tessellator が入ります。
このテセレータというのは、位置的にも機能的にもラスタライザに非常に近いもの
なのかもしれません。
そう思えば固定機能であることも納得できます。
DomainShader はラスタライズ後に走る Pixsel Shader に相当し、規模も走る
スレッド数も動的に変化します。
テクスチャによってエフェクトが入る点も似ています。
DomainShader は Displacement Map を読み込むので、Pixsel Shader に次いで
テクスチャアクセスを多用するシェーダーとなりそうです。
そもそも、動的にサイズが変化するユニットだからこそイメージマップを利用する
意味があるわけで、必然というか当たり前のことなのかもしれません。
単一のパイプライン中に Tessellator と Rasterizer の 2ヶ所でデータの増幅が
行われます。本当は Geometry Shader でも可能なので 3ヶ所です。
逆にポリゴン減らすのは、生成後のプリミティブ単位で実行される Geometry Shader
でしか出来ないと思われます。
もしマップによってメッシュに穴を開けたいなら、GeometryShader でもテクスチャを
サンプリングするか、Domain Shader からパラメータを渡すことになるのでしょう。
ピクセルを減らすこと自体は PiselShader でも clip (discard) で可能です。
Vertex 単位で走る VertexShader や、Pixel 単位で走る PixelShader を考えると、
GeometryShader は PrimitiveShader といった方が理解しやすいかもしれません。
となると HullShader は PatchShader です。
DomainShader は Post Vertex Shader といった位置づけで、Tessellation 後の
VertexShader に相当します。
本来の VertexShader は Pre Vertex Shader。
VS Vertex Shader → Pre Vertex Shader HS Hull Shader → Patch Shader (Pre Primitive Shader) TS [[Tessellator]] DS Domain Shader → Post Vertex Shader GS Geometry Shader → Primitive Shader RS [[Rasterizer]] PS Pixel Shader
だいぶすっきりしました。
でも全部略称が PS になって区別できないからやっぱりだめです。
Tessellator や Displacement Map によって 3D データの作り方は今後変わっていく
と思われます。
3D だけでなく、ベクタフォントを GPU で直接描画するなど 2D 面でも応用できる
かもしれません。
関連エントリ
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
2009/05/10
VAIO type P + Windows7 RC で Direct3D 11
Windows7 RC を入れた type P はAero がそれなりに動いているので印象が変わりました。
現在透明感 off で使用しています。
・個人設定→ウィンドウの色→「透明感を有効にする」のチェックを外す
半透明ではないもののウィンドウの重なりがハードウエア描画なので速いです。
たとえばウィンドウ移動とか最小化のアニメーションとかそこそこスムーズに
できますし(たまにかくつくけど)、タスクバーにマウスカーソルを乗せると
ウィンドウの縮小イメージもポップアップで出ます。
ウィンドウの中の描画速度は変わらないけど、操作の印象は良くなりました。
たまに背景の描画が追いつかないのか真っ黒に崩れることがあります。
フレームバッファが消えてしまった状態ですが、以前から同じ症状があった気がするので
また違う問題かもしれません。
Aero が動くということは D3D も動くようになったということ。
GMA500 がどの Feature Level まで対応しているのか Direct3D11 で試してみました。
結果は残念ながら FEATURE_LEVEL_9_1 ~ 3 も未対応。
ハードウエアでは動作しませんでした。
Direct3D11 サンプルや一部のアプリケーションが動くのは、ソフトウエアレンダラ
WARP Device を呼び出しているからです。
D3D11CreateDevice~() の D3D_DRIVER_TYPE_HARDWARE でエラーが出た場合、
D3D_DRIVER_TYPE_WARP できちんと作り直しているアプリは動きます。
D3D11 サンプルが動いて D3D10 が動かないのはおそらくアプリが WARP に対応していないから。
D3D10 LEVEL_9_1 ~ に対応していないということは Direct2D を使う場合もおそらく
CPU レンダラになります。
ハードウエア対応は今後に期待。現状未対応でもとりあえず動くのは WARP のおかげです。
●結論
GPU は Direct3D 9 3.0 まで対応しているものの、
現状では Direct3D10/11 の下位モードの Level9 でも動作せず。
WARP なら動く。
関連エントリ
・VAIO Type P の Windows7 RC は Aero が有効
現在透明感 off で使用しています。
・個人設定→ウィンドウの色→「透明感を有効にする」のチェックを外す
半透明ではないもののウィンドウの重なりがハードウエア描画なので速いです。
たとえばウィンドウ移動とか最小化のアニメーションとかそこそこスムーズに
できますし(たまにかくつくけど)、タスクバーにマウスカーソルを乗せると
ウィンドウの縮小イメージもポップアップで出ます。
ウィンドウの中の描画速度は変わらないけど、操作の印象は良くなりました。
たまに背景の描画が追いつかないのか真っ黒に崩れることがあります。
フレームバッファが消えてしまった状態ですが、以前から同じ症状があった気がするので
また違う問題かもしれません。
Aero が動くということは D3D も動くようになったということ。
GMA500 がどの Feature Level まで対応しているのか Direct3D11 で試してみました。
結果は残念ながら FEATURE_LEVEL_9_1 ~ 3 も未対応。
ハードウエアでは動作しませんでした。
Direct3D11 サンプルや一部のアプリケーションが動くのは、ソフトウエアレンダラ
WARP Device を呼び出しているからです。
D3D11CreateDevice~() の D3D_DRIVER_TYPE_HARDWARE でエラーが出た場合、
D3D_DRIVER_TYPE_WARP できちんと作り直しているアプリは動きます。
D3D11 サンプルが動いて D3D10 が動かないのはおそらくアプリが WARP に対応していないから。
D3D10 LEVEL_9_1 ~ に対応していないということは Direct2D を使う場合もおそらく
CPU レンダラになります。
ハードウエア対応は今後に期待。現状未対応でもとりあえず動くのは WARP のおかげです。
●結論
GPU は Direct3D 9 3.0 まで対応しているものの、
現状では Direct3D10/11 の下位モードの Level9 でも動作せず。
WARP なら動く。
関連エントリ
・VAIO Type P の Windows7 RC は Aero が有効
2009/09/23
RADEON HD 5870 と DirectX 11
Direct3D 11 対応 GPU RADEON HD 5870/5850 が発表されました。
Windows7 発売に間に合うし、Direct3D 11 SDK も直前に RTM しています。
足並みが揃った良いタイミングです。
あとは安定して入手できるかどうか、ドライバが間に合うかどうかでしょう。
・AMD、業界初DirectX 11対応GPU「Radeon HD 5800」シリーズ ~上位モデルは1,600SP搭載の2.7TFLOPS
最近は GPU の世代が代わり 機能が増えても一時的なパフォーマンスの低下は見られなく
なりました。GeForce 6800 や 8800 の時もそうでしたがむしろ倍のスコアを出しています。
速度を求める場合も機能が目的でも、安心して手を出せるのは嬉しいところです。
Direct3D 10 → Direct3D 11 の更新は API 的なデザインの変更は少ないものの、
Direct3D 8 → Direct3D 9 と同じように増えた機能はかなりあります。
・汎用性の向上 Compute Shader
・グラフィックス機能の進化 Tessellation, BC6H/BC7
・管理機能の強化 Dynamic Shader Linkage
・スレッド対応 Deferred Context
等
一見地味な Dynamic Shader Linkage や Deferred Context も、開発者にとっては
大変嬉しい機能です。
Dynamic Shader Linkage は描画発行時にシェーダー内の飛び先を決定することが出来ます。
いわば関数ポインタのようなもので、シェーダーの種類を増やさずに機能の切り替えを
容易にしてくれます。
Deferred Context は他のスレッドが発行した描画命令を Display List としてバッファリングし、
コンテキストに矛盾が生じないようにします。
シェーダーの機能切り替えも効率よいスレッドの活用も、これまではアプリケーション側で工夫して
実現する必要がありました。
つまり DirectX 11 では従来 CPU 側で行ってきたいくつかの分野もアクセラレーションの
対象となっているといえます。GPGPU に限らず GPU の役割は徐々に広がっているようです。
今後 CPU/GPU もお互いに相手の機能を取り込むだけではなく、より連携しやすいような歩み寄りも
必要ではないでしょうか。例えば CPU にも積極的に GPU 連携のための命令が追加されるとか。
SSE5 の half 型変換命令はその一つの好例かもしれません。
DirectX/GPU 年表 も更新しました。
関連エントリ
・DirectX SDK August 2009 の解説と Direct3D 11 RTM
・Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
・Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
Windows7 発売に間に合うし、Direct3D 11 SDK も直前に RTM しています。
足並みが揃った良いタイミングです。
あとは安定して入手できるかどうか、ドライバが間に合うかどうかでしょう。
・AMD、業界初DirectX 11対応GPU「Radeon HD 5800」シリーズ ~上位モデルは1,600SP搭載の2.7TFLOPS
最近は GPU の世代が代わり 機能が増えても一時的なパフォーマンスの低下は見られなく
なりました。GeForce 6800 や 8800 の時もそうでしたがむしろ倍のスコアを出しています。
速度を求める場合も機能が目的でも、安心して手を出せるのは嬉しいところです。
Direct3D 10 → Direct3D 11 の更新は API 的なデザインの変更は少ないものの、
Direct3D 8 → Direct3D 9 と同じように増えた機能はかなりあります。
・汎用性の向上 Compute Shader
・グラフィックス機能の進化 Tessellation, BC6H/BC7
・管理機能の強化 Dynamic Shader Linkage
・スレッド対応 Deferred Context
等
一見地味な Dynamic Shader Linkage や Deferred Context も、開発者にとっては
大変嬉しい機能です。
Dynamic Shader Linkage は描画発行時にシェーダー内の飛び先を決定することが出来ます。
いわば関数ポインタのようなもので、シェーダーの種類を増やさずに機能の切り替えを
容易にしてくれます。
Deferred Context は他のスレッドが発行した描画命令を Display List としてバッファリングし、
コンテキストに矛盾が生じないようにします。
シェーダーの機能切り替えも効率よいスレッドの活用も、これまではアプリケーション側で工夫して
実現する必要がありました。
つまり DirectX 11 では従来 CPU 側で行ってきたいくつかの分野もアクセラレーションの
対象となっているといえます。GPGPU に限らず GPU の役割は徐々に広がっているようです。
今後 CPU/GPU もお互いに相手の機能を取り込むだけではなく、より連携しやすいような歩み寄りも
必要ではないでしょうか。例えば CPU にも積極的に GPU 連携のための命令が追加されるとか。
SSE5 の half 型変換命令はその一つの好例かもしれません。
DirectX/GPU 年表 も更新しました。
関連エントリ
・DirectX SDK August 2009 の解説と Direct3D 11 RTM
・Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
・Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
2008/11/12
Direct3D11/DirectX11 (7) テセレータの流れの基本部分
● ComputeShader 用の Append Buffer 作成
何度も DebugLayer に怒られつつテストしてみました。
Buffer
D3D11_USAGE_DEFAULT
D3D11_BIND_UNORDERED_ACCESS
D3D11_RESOURCE_MISC_BUFFER_STRUCTURED
StructureByteStride= 4
UAV
DXGI_FORMAT_UNKNOWN
D3D11_BUFFER_UAV_FLAG_APPEND
バッファ内容の変化が観測できず、まだ動作がつかめていません。
うまく動いていないところや使い方の疑問点もあります。
・ID3D11DeviceContext::CSSetUnorderedAccessViews() に 2つ以上の UAV を渡すと
シェーダーが落ちる。サンプル HDRToneMappingCS11 では 1つしか渡していない。
・CSSetUnorderedAccessViews() の 3 番目の引数の意味がわからない。
サンプルでは 2番目のリストのアドレスを UINT* で渡しているが、これが正しい
とは思えない。
●テセレータ
これも D3D11 での大きな追加機能の一つです。
HullShader と DomainShader を設定することで、その間を繋ぐ Tessellator は
勝手に呼び出されるようになります。
VertexShader と PixelShader の間に、暗黙のうちに Rasterizer が呼ばれるのと
よく似ています。機能も似ています。
GS と HS → DS はオプションなので、パイプラインは下記の 4 通り (+2) です。
Tessellator を Enable にした場合使用できるプリミティブは Patch のみとなります。
PATCH は新しく追加された Primitive Topology で、1~32 までの頂点を入力可能です。
Patch の入力頂点は、主に分割時のカーブを求めるためのコントロールポイントとして
使われます。
従来 TriangleListAdj でも最大 6頂点までだったので、入力頂点数は大幅に増えました。
試していませんが DebugLayer のメッセージを見ると GeometryShader で
Patch を直接受け取れる可能性があります。もし使えるなら汎用の多入力頂点としても
応用できそうです。
HullShader は Tessellator に渡すパラメータの生成を行います。
入力された頂点 (コントロールポイント) 毎に走る関数と、パッチ単位で一回だけ走る
関数の 2つエントリポイントがあります。
VertexShader と GeometryShader がセットになったような感じですが、どちらも
1プリミティブ(patch) 分の全頂点に自由にアクセスすることが可能です。
(1) VertexShader が頂点単位に走る
(2) Patchを構成する全頂点(ControlPoint)がキャッシュされたら HullShader を
呼び出す。
(3) HullShader は 2つの関数が存在する
- Patch 単位で走る × 1
- 頂点(ControlPoint) 単位で走る × 頂点数
HullShader が出力すべき必須パラメータは SV_TessFactor と SV_InsideTessFactor
です。これは Path 単位で走る "patchconstantfunc" が設定するようです。
Rasterizer の前に SV_Position が確定していなければならないのと同じように、
Tessellator の前に TessFactor も必須です。
(4) Tessellator が頂点を生成する。
(5) Tessellator の出力頂点毎に DomainShader が走る
DomainShader は、Tessellator の出力である SV_DomainLocation と、
HullShader の出力パラメータをそのまま受け取ります。
DomainShader の役割は、HullShader の出力と Tessellator の出力を元に、正しい
頂点を生成することのようです。
HullShader と違って、Tessellator の実行が完了してから走るわけではありません。
平行して生成される毎にストリームとして呼び出されるようです。
その点 VertexShader に近いといえます。
(6) 1プリミティブ分頂点が生成されたら GeometryShader を呼び出す。
まだ詳しく使い込んでない&調べてませんが、HullShader とテセレータは若干
並列動作しているのかもしれません。
ただし DomainShader が走る前には、依存している HullShader は完了している
必要があります。
全コントロールポイントの情報が集まってから各シェーダーに渡されるなど、
シェーダー間で受け渡すデータ量が大幅に増えていることがわかります。
ComputeShader で追加された Append / Consume Buffer の仕組みは、このように
シェーダー間で複雑なデータを受け渡す必然性から設けられたものかもしれません。
単体で見たときはいまいち機能や用途が思い浮かばなかったのですが、HS, DS, GS の
流れを見ていると Append / Consume Buffer の存在意義が見えてきた気がします。
パイプラインが複雑で扱うデータも増えました。
それでも GPU / シェーダーで実行するメリットは、メインメモリを介さず、
CPU を介さずに頂点の増減をストリーム内で閉じたまま扱えることです。
関連エントリ
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
何度も DebugLayer に怒られつつテストしてみました。
Buffer
D3D11_USAGE_DEFAULT
D3D11_BIND_UNORDERED_ACCESS
D3D11_RESOURCE_MISC_BUFFER_STRUCTURED
StructureByteStride= 4
UAV
DXGI_FORMAT_UNKNOWN
D3D11_BUFFER_UAV_FLAG_APPEND
AppendStructuredBuffer<uint> a; [numthreads(1,1,1)] void main( uint3 tid : SV_DispatchThreadID ) { a.Append( tid.x ); }
バッファ内容の変化が観測できず、まだ動作がつかめていません。
うまく動いていないところや使い方の疑問点もあります。
・ID3D11DeviceContext::CSSetUnorderedAccessViews() に 2つ以上の UAV を渡すと
シェーダーが落ちる。サンプル HDRToneMappingCS11 では 1つしか渡していない。
・CSSetUnorderedAccessViews() の 3 番目の引数の意味がわからない。
サンプルでは 2番目のリストのアドレスを UINT* で渡しているが、これが正しい
とは思えない。
●テセレータ
これも D3D11 での大きな追加機能の一つです。
HullShader と DomainShader を設定することで、その間を繋ぐ Tessellator は
勝手に呼び出されるようになります。
VertexShader と PixelShader の間に、暗黙のうちに Rasterizer が呼ばれるのと
よく似ています。機能も似ています。
GS と HS → DS はオプションなので、パイプラインは下記の 4 通り (+2) です。
VS → (Rasterizer) PS VS → GS → (Rasterizer) PS VS → HS → (Tessellator) DS → (Rasterizer) PS VS → HS → (Tessellator) DS → GS → (Rasterizer) PS StreamOutput: VS → GS → SO VS → HS → (Tessellator) DS → GS → SO
Tessellator を Enable にした場合使用できるプリミティブは Patch のみとなります。
PATCH は新しく追加された Primitive Topology で、1~32 までの頂点を入力可能です。
・Point List ・Line List, Strip, ListAdj, StripAdj ・Triangle List, Strip, ListAdj, StripAdj ・Patch 1~32
Patch の入力頂点は、主に分割時のカーブを求めるためのコントロールポイントとして
使われます。
従来 TriangleListAdj でも最大 6頂点までだったので、入力頂点数は大幅に増えました。
試していませんが DebugLayer のメッセージを見ると GeometryShader で
Patch を直接受け取れる可能性があります。もし使えるなら汎用の多入力頂点としても
応用できそうです。
HullShader は Tessellator に渡すパラメータの生成を行います。
入力された頂点 (コントロールポイント) 毎に走る関数と、パッチ単位で一回だけ走る
関数の 2つエントリポイントがあります。
VertexShader と GeometryShader がセットになったような感じですが、どちらも
1プリミティブ(patch) 分の全頂点に自由にアクセスすることが可能です。
(1) VertexShader が頂点単位に走る
(2) Patchを構成する全頂点(ControlPoint)がキャッシュされたら HullShader を
呼び出す。
(3) HullShader は 2つの関数が存在する
- Patch 単位で走る × 1
- 頂点(ControlPoint) 単位で走る × 頂点数
HullShader が出力すべき必須パラメータは SV_TessFactor と SV_InsideTessFactor
です。これは Path 単位で走る "patchconstantfunc" が設定するようです。
Rasterizer の前に SV_Position が確定していなければならないのと同じように、
Tessellator の前に TessFactor も必須です。
(4) Tessellator が頂点を生成する。
(5) Tessellator の出力頂点毎に DomainShader が走る
DomainShader は、Tessellator の出力である SV_DomainLocation と、
HullShader の出力パラメータをそのまま受け取ります。
DomainShader の役割は、HullShader の出力と Tessellator の出力を元に、正しい
頂点を生成することのようです。
HullShader と違って、Tessellator の実行が完了してから走るわけではありません。
平行して生成される毎にストリームとして呼び出されるようです。
その点 VertexShader に近いといえます。
(6) 1プリミティブ分頂点が生成されたら GeometryShader を呼び出す。
まだ詳しく使い込んでない&調べてませんが、HullShader とテセレータは若干
並列動作しているのかもしれません。
ただし DomainShader が走る前には、依存している HullShader は完了している
必要があります。
─┬ per control point HS ────────→ DomainShader └ PatchConstantFunc HS → Tessellator → DomainShader
全コントロールポイントの情報が集まってから各シェーダーに渡されるなど、
シェーダー間で受け渡すデータ量が大幅に増えていることがわかります。
ComputeShader で追加された Append / Consume Buffer の仕組みは、このように
シェーダー間で複雑なデータを受け渡す必然性から設けられたものかもしれません。
単体で見たときはいまいち機能や用途が思い浮かばなかったのですが、HS, DS, GS の
流れを見ていると Append / Consume Buffer の存在意義が見えてきた気がします。
パイプラインが複雑で扱うデータも増えました。
それでも GPU / シェーダーで実行するメリットは、メインメモリを介さず、
CPU を介さずに頂点の増減をストリーム内で閉じたまま扱えることです。
関連エントリ
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2008/11/14
Direct3D11/DirectX11 (8) テセレータの動作
●固定機能テセレータ
DomainShader で、Tessellator の出力 uv のみを WIRE 描画したものです。
TessFactor = 1, 1, 1, 1
InsideTessFactor = 1, 1

TessFactor = 3, 3, 3, 3
InsideTessFactor = 3, 3

TessFactor = 3, 3, 3, 3
InsideTessFactor = 8, 8

パッチ毎に分割数が異なる可能性があるため内部とエッジの係数を個別に指定します。
上の 3333-33 と 3333-88 は繋がります。
固定機能であるテセレータは、TessFactor に応じて上記の uv パターンを生成し、
必要分の頂点を出力しているだけだと考えられます。
実際の曲面を作るのは HullShader と DomainShader の仕事です。
●ベジェ
実際にシンプルなベジェ曲面を生成してみます。
単純化するため入力はすべてコントロールポイントとします。

VertexShader は 16個のコントロールポイント定義のみ。
HullShader は TessFactor 設定以外に特にやることがありません。
VS からの出力をスルーし、もう 1つは TessFactor を定義します。
DomainShader で、生成された頂点の実際の座標を求めます。
同時に View Projection の変換もします。
呼び出し側。GeometryShader は使用していません。
関連エントリ
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
DomainShader で、Tessellator の出力 uv のみを WIRE 描画したものです。
TessFactor = 1, 1, 1, 1
InsideTessFactor = 1, 1

TessFactor = 3, 3, 3, 3
InsideTessFactor = 3, 3

TessFactor = 3, 3, 3, 3
InsideTessFactor = 8, 8

パッチ毎に分割数が異なる可能性があるため内部とエッジの係数を個別に指定します。
上の 3333-33 と 3333-88 は繋がります。
固定機能であるテセレータは、TessFactor に応じて上記の uv パターンを生成し、
必要分の頂点を出力しているだけだと考えられます。
実際の曲面を作るのは HullShader と DomainShader の仕事です。
struct DS_OUTPUT { float4 vPos : SV_Position; }; [domain("quad")] DS_OUTPUT main( HS_POINT_DATA ip, float2 uv : SV_DomainLocation, const OutputPatch<HS_OUTPUT,4> bpatch ) { DS_OUTPUT dout; uv+= float2( -0.5, -0.5 ); uv*= float2( 0.5, 0.5 ); dout.vPos= float4( uv.x, -uv.y, 0, 1 ); return dout; }
●ベジェ
実際にシンプルなベジェ曲面を生成してみます。
単純化するため入力はすべてコントロールポイントとします。

VertexShader は 16個のコントロールポイント定義のみ。
// vs.hlsl float4 main( uint vid : SV_VertexID ) : VSPOS { const float4 v[]= { { -2, 2, 2, 1, }, { -1, 2, 0, 1, }, { 1, 2, 0, 1, }, { 2, 2, 2, 1, }, { -2, 1, 0, 1, }, { -1, 1, 0, 1, }, { 1, 1, 0, 1, }, { 2, 1, 0, 1, }, { -2, -1, 0, 1, }, { -1, -1, 0, 1, }, { 1, -1, 0, 1, }, { 2, -1, 0, 1, }, { -2, -2, 2, 1, }, { -1, -2, 0, 1, }, { 1, -2, 0, 1, }, { 2, -2, 2, 1, }, }; return v[vid]; }
HullShader は TessFactor 設定以外に特にやることがありません。
VS からの出力をスルーし、もう 1つは TessFactor を定義します。
// hs.hlsl struct VS_OUTPUT { float4 vPos : VSPOS; }; struct HS_OUTPUT { float3 vPos : TSPOS; }; struct HS_DATA { float Edge[4] : SV_TessFactor; float Inside[2] : SV_InsideTessFactor; }; HS_DATA func( InputPatch<VS_OUTPUT,16> ip, uint patchID : SV_PrimitiveID ) { HS_DATA pdata; pdata.Edge[0]= 32.0; pdata.Edge[1]= 32.0; pdata.Edge[2]= 32.0; pdata.Edge[3]= 32.0; pdata.Inside[0]= 32.0; pdata.Inside[1]= 32.0; return pdata; } [domain("quad")] [partitioning("integer")] [outputtopology("triangle_cw")] [outputcontrolpoints(16)] [patchconstantfunc("func")] HS_OUTPUT main( InputPatch<VS_OUTPUT,16> ip, uint id : SV_OutputControlPointID, uint patchID : SV_PrimitiveID ) { HS_OUTPUT odata; odata.vPos= ip[id].vPos.xyz; // そのまま渡すだけ return odata; }
DomainShader で、生成された頂点の実際の座標を求めます。
同時に View Projection の変換もします。
// ds.hlsl struct HS_DATA { float Edge[4] : SV_TessFactor; float Inside[2] : SV_InsideTessFactor; }; struct HS_OUTPUT { float3 vPos : TSPOS; }; struct DS_OUTPUT { float4 vPos : SV_Position; }; cbuffer perFrame { float4x4 WVP; // View Projection }; float4 Bezier0( float t ) { float t2= 1.0-t; return float4( t2*t2*t2, t2*t2* t*3, t2* t* t*3, t* t* t ); } float3 UVtoPosition( const OutputPatch<HS_OUTPUT,16> p, float2 uv ) { float4 u= Bezier0( uv.x ); float4 v= Bezier0( uv.y ); float3 r; r = v.x*(u.x*p[ 0].vPos+u.y*p[ 1].vPos+u.z*p[ 2].vPos+u.w*p[ 3].vPos); r+= v.y*(u.x*p[ 4].vPos+u.y*p[ 5].vPos+u.z*p[ 6].vPos+u.w*p[ 7].vPos); r+= v.z*(u.x*p[ 8].vPos+u.y*p[ 9].vPos+u.z*p[10].vPos+u.w*p[11].vPos); r+= v.w*(u.x*p[12].vPos+u.y*p[13].vPos+u.z*p[14].vPos+u.w*p[15].vPos); return r; } [domain("quad")] DS_OUTPUT main( HS_DATA ip, float2 uv : SV_DomainLocation, const OutputPatch<HS_OUTPUT,16> bpatch ) { DS_OUTPUT dout; float3 pos= UVtoPosition( bpatch, uv ); dout.vPos= mul( float4( pos.xyz, 1 ), WVP ); return dout; }
呼び出し側。GeometryShader は使用していません。
iContext->RSSetState( iRS_WIREFRAME ); iContext->VSSetShader( iVS, NULL, 0 ); iContext->HSSetShader( iHS, NULL, 0 ); iContext->DSSetConstantBuffers( 0, 1, &iPerFrame ); iContext->DSSetShader( iDS, NULL, 0 ); iContext->PSSetShader( iPS, NULL, 0 ); iContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_16_CONTROL_POINT_PATCHLIST ); iContext->Draw( 16, 0 );
関連エントリ
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2009/03/25
DirectX SDK March 2009
2009年初の新しい SDK です。
・DirectX SDK (March 2009)
大きなトピックは Direct2D/DirectWrite の Technical Preview が含まれたこと。
Windows7 beta + Windows7 SDK beta でないと試すことが出来なかった
Direct2D / DirectWrite が Vista でも使えるようになりました。
ドキュメント類は以前から MSDN に上がっているのでそちらを参照することになります。
インストールによって d2d1_beta.dll, dwrite_beta.dll が入りました。
これでおそらく D2D/DWrite だけなら Windows7 SDK が無くてもテストできると思います。
ヘッダ類もこちらの方が新しいようです。コメントが増えて新しい定義もあります。
その他 DXGI1.1 も入りました。元々ヘッダは含まれてましたが、dxgi_beta.dll / lib が
追加されてドキュメントも統合されたようです。
Direct3D11 TechPreview の方はあまり大きな変化があったようには見えませんが、
細かく変更が入っています。
たとえばヘッダには CD3D11_RECT, CD3D11_DEPTH_STENCIL_DESC,
CD3D11_BLEND_DESC 等のヘルパークラスが多数追加されているようです。
Direct2D と同じようにデフォルト値による初期化がサポートされています。
デフォルト値を示すオーバーロード用の type は CD3D11_DEFAULT です。
マニュアルでは Shader Model 5 の Attributes に attrib, maxtessfactor,
earlydepthstencil が追加。Intrinsic Functions には
countbits, EvaluateAttributeAtControid, EvaluateAttributeAtSample
が増えています。
またヘッダを見ないと使い方が分からなかった D3DCompiler のマニュアルも入りました。
Shader の項目には Reflection 関連のインターフェースが追加されています。
このあたりヘッダを見てもかなり手が入っていることが分かります。
まだ Effect (fx) はありませんが進展は見られます。
その他マニュアルには載ってませんが D3D11_CREATE_DEVICE_BGRA_SUPPORT
も目新しいところです。
関連エントリ
・Direct2D (7) Tessellate の 2
・Direct2D と Direct3D10.1 の下位互換
・Windows7 リモートデスクトップと Direct3D
・Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
・DirectX SDK (March 2009)
大きなトピックは Direct2D/DirectWrite の Technical Preview が含まれたこと。
Windows7 beta + Windows7 SDK beta でないと試すことが出来なかった
Direct2D / DirectWrite が Vista でも使えるようになりました。
ドキュメント類は以前から MSDN に上がっているのでそちらを参照することになります。
インストールによって d2d1_beta.dll, dwrite_beta.dll が入りました。
これでおそらく D2D/DWrite だけなら Windows7 SDK が無くてもテストできると思います。
ヘッダ類もこちらの方が新しいようです。コメントが増えて新しい定義もあります。
その他 DXGI1.1 も入りました。元々ヘッダは含まれてましたが、dxgi_beta.dll / lib が
追加されてドキュメントも統合されたようです。
Direct3D11 TechPreview の方はあまり大きな変化があったようには見えませんが、
細かく変更が入っています。
たとえばヘッダには CD3D11_RECT, CD3D11_DEPTH_STENCIL_DESC,
CD3D11_BLEND_DESC 等のヘルパークラスが多数追加されているようです。
Direct2D と同じようにデフォルト値による初期化がサポートされています。
デフォルト値を示すオーバーロード用の type は CD3D11_DEFAULT です。
マニュアルでは Shader Model 5 の Attributes に attrib, maxtessfactor,
earlydepthstencil が追加。Intrinsic Functions には
countbits, EvaluateAttributeAtControid, EvaluateAttributeAtSample
が増えています。
またヘッダを見ないと使い方が分からなかった D3DCompiler のマニュアルも入りました。
Shader の項目には Reflection 関連のインターフェースが追加されています。
このあたりヘッダを見てもかなり手が入っていることが分かります。
まだ Effect (fx) はありませんが進展は見られます。
その他マニュアルには載ってませんが D3D11_CREATE_DEVICE_BGRA_SUPPORT
も目新しいところです。
関連エントリ
・Direct2D (7) Tessellate の 2
・Direct2D と Direct3D10.1 の下位互換
・Windows7 リモートデスクトップと Direct3D
・Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2008/11/07
Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
Direct3D11 (DirectX11) はマルチスレッドを意識した設計が行われています。
この場合のスレッドとは CPU 側の話で、複数 core CPU が効率よく D3D API を
使用できるようになっています。
D3D10 以前のスレッド対応機能は、複数のスレッドから呼び出してもとりあえず
問題が起こらない、というレベルのものでした。
ハードウエアスレッドでの速度効率はあまり考えられていなかったといえます。
D3D11 では次の点が改良されています。
・描画やステート変更などの命令発行を複数持てる。CommandList と呼ばれる。
Push バッファをコンテキスト毎に複数持てる。(deferred context)
・描画スレッド以外でも GPU リソースを作成できる。
従来は単一のコマンドバッファに API 単位で同期しつつアクセスが行われていた
ためスレッド間でブロックします。D3D11 はバッファリングで不要なブロッキングと
コンテキストの矛盾を回避します。
個別の CommandList (Display List) の実行には新しい GPU やドライバの機能を
必要としますが、命令の記録を API でエミュレートできるため、低い FeatureLevel
でも動作するようです。エミュレートは実行時にメモリコピーが発生する可能性が
あるため、single core CPU の場合はおそらくあまりメリットがないと思われます。
実際 D3D11_CREATE_DEVICE_SINGLETHREADED を指定すると
CreateDeferredContext() は失敗するようです。
これらの機能に対応するため D3D の Device Interface は Context が分離されました。
(1) ID3D11Device
(2) ID3D11DeviceContext
(1) の ID3D11Device 自体は Create 命令だらけになっています。
描画やステート変更は (2) の ID3D11DeviceContext に移動しました。
言ってしまえば、スレッドセーフでシェア可能な API とスレッドローカルな API に
分かれたわけです。
ID3D11Device を使ったリソースの作成はスレッドセーフであり特別な管理は不要です。
ID3D11DeviceContext は API 呼び出しを行うスレッド毎に所有する必要があります。
ID3D11DeviceContext の生成には次の命令を使います。
ID3D11Device::GetImmediateContext()
ID3D11Device::CreateDeferredContext()
ImmediateContext は従来の API 呼び出しと同等で、プッシュバッファに記録された
命令は必要に応じて kick されます。
DeferredContext の呼び出しは CommandList (ID3D11CommandList) に記録され、
ExecuteCommandList() を実行するまで保留します。
ExecuteCommandList() の呼び出しは、ImmediateContext() が行います。
●描画メインスレッド
1. GetImmediateContext() で作成した ID3D11DeviceContext を使う。
2. 他のスレッドが作成した CommandList を ExecuteCommandList() する。
●サブスレッド
1. CreateDeferredContext() で作成した ID3D11DeviceContext を使用する。
2. FinishCommandList() で ID3D11CommandList を受け取る。
シェーダーの種類も増えたので、ID3D11DeviceContext の命令も増えています。
CS~ CoputeShader
DS~ DomainShader
GS~ GeometryShader
HS~ HullShader
IA~ InputAssembler
OM~ OutputManager
PS~ PixelShader
RS~ Rasterizer
VS~ VertexShader
SO~ StreamOutput
OM~ OutputManager
HS DS GS VS PS の 6種類のシェーダーそれぞれに次の 8命令が用意されています。
~GetConstantBuffers()
~SetConstantBuffers()
~GetSamplers()
~SetSamplers()
~GetShader()
~SetShader()
~GetShaderResource()
~SetShaderResource()
CS のみ CSGetUnorderdAccessViews()/CSSetUnorderdAccessViews() の特殊な
2命令があります。
これだけで 6 x 8 + 2 = 50命令
関連エントリ
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
この場合のスレッドとは CPU 側の話で、複数 core CPU が効率よく D3D API を
使用できるようになっています。
D3D10 以前のスレッド対応機能は、複数のスレッドから呼び出してもとりあえず
問題が起こらない、というレベルのものでした。
ハードウエアスレッドでの速度効率はあまり考えられていなかったといえます。
D3D11 では次の点が改良されています。
・描画やステート変更などの命令発行を複数持てる。CommandList と呼ばれる。
Push バッファをコンテキスト毎に複数持てる。(deferred context)
・描画スレッド以外でも GPU リソースを作成できる。
従来は単一のコマンドバッファに API 単位で同期しつつアクセスが行われていた
ためスレッド間でブロックします。D3D11 はバッファリングで不要なブロッキングと
コンテキストの矛盾を回避します。
個別の CommandList (Display List) の実行には新しい GPU やドライバの機能を
必要としますが、命令の記録を API でエミュレートできるため、低い FeatureLevel
でも動作するようです。エミュレートは実行時にメモリコピーが発生する可能性が
あるため、single core CPU の場合はおそらくあまりメリットがないと思われます。
実際 D3D11_CREATE_DEVICE_SINGLETHREADED を指定すると
CreateDeferredContext() は失敗するようです。
これらの機能に対応するため D3D の Device Interface は Context が分離されました。
(1) ID3D11Device
(2) ID3D11DeviceContext
(1) の ID3D11Device 自体は Create 命令だらけになっています。
描画やステート変更は (2) の ID3D11DeviceContext に移動しました。
言ってしまえば、スレッドセーフでシェア可能な API とスレッドローカルな API に
分かれたわけです。
ID3D11Device を使ったリソースの作成はスレッドセーフであり特別な管理は不要です。
ID3D11DeviceContext は API 呼び出しを行うスレッド毎に所有する必要があります。
ID3D11DeviceContext の生成には次の命令を使います。
ID3D11Device::GetImmediateContext()
ID3D11Device::CreateDeferredContext()
ImmediateContext は従来の API 呼び出しと同等で、プッシュバッファに記録された
命令は必要に応じて kick されます。
DeferredContext の呼び出しは CommandList (ID3D11CommandList) に記録され、
ExecuteCommandList() を実行するまで保留します。
ExecuteCommandList() の呼び出しは、ImmediateContext() が行います。
●描画メインスレッド
1. GetImmediateContext() で作成した ID3D11DeviceContext を使う。
2. 他のスレッドが作成した CommandList を ExecuteCommandList() する。
●サブスレッド
1. CreateDeferredContext() で作成した ID3D11DeviceContext を使用する。
2. FinishCommandList() で ID3D11CommandList を受け取る。
シェーダーの種類も増えたので、ID3D11DeviceContext の命令も増えています。
CS~ CoputeShader
DS~ DomainShader
GS~ GeometryShader
HS~ HullShader
IA~ InputAssembler
OM~ OutputManager
PS~ PixelShader
RS~ Rasterizer
VS~ VertexShader
SO~ StreamOutput
OM~ OutputManager
HS DS GS VS PS の 6種類のシェーダーそれぞれに次の 8命令が用意されています。
~GetConstantBuffers()
~SetConstantBuffers()
~GetSamplers()
~SetSamplers()
~GetShader()
~SetShader()
~GetShaderResource()
~SetShaderResource()
CS のみ CSGetUnorderdAccessViews()/CSSetUnorderdAccessViews() の特殊な
2命令があります。
これだけで 6 x 8 + 2 = 50命令
関連エントリ
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2008/09/19
Direct3D11 Compute Shader など
再び Gamefest2008 のあたりから D3D11 関連。
・Gamefest 2008 Presentations
●Compute Shader
ポストフィルタなど Compute Shader の想定されている用途や、
RWTexture2D/RWBuffer が PixelShader でも使えることなどから、
Compute Shader は Pixel Shader に近いものだと考えられます。
実際に現在の GPU でも、AMD Stream SDK の CAL で走るプログラムは
PixelShader 相当だったため一致しています。
Compute Shader は 32KByte のシェアドメモリにアクセス可能です。
2048 float4 = 8192 float = 32KByte
共有メモリと言うよりも、スレッド間で共有されるレジスタ Shared Register と
表現されているようです。
RADEON HD4870 の CAL を使った global buffer (shared local) は実測で
2048 vector、ちょうど 32KByte でした。
これが Shared Memory (Register) 相当と考えられるためほぼ一致しています。
任意の読み書きと Shared Register 以外の特徴として Atomic 命令があります。
32bit 値の read-modify-write に対応しているようです。
命令も Windows のような Interlocked~()
これを利用してスレッド同期が可能となり、排他的なメモリへアクセスなどが
できるようです。
AMD CAL では見あたりませんでしたが、CUDA では 32/64bit の Atomic 命令を
備えているようです。
AMD CAL でも Global Buffer へのアクセス、たとえば
「add g[0].x, g[0].x, r1.x」が Atomic なら代用できますが
そうではないようです。
g[0].x は毎回異なる値となりました。
● API
Direct3D11 は Direct3D10 とほぼ同じ API セットを持ち、機能拡張されている
ものとなるそうです。
詳細は SDK のリリースを待たないといけませんが、おそらく Direct3D8 と
Direct3D9 の関係に近いものだと考えられます。
DirectX7 までのベースとなる DirectDraw を捨てて、Direct3D8 は作り直されました。
その後の Direct3D9 は完全に互換性があるとはいえないものの、考え方や設計は
Direct3D8 とほとんど同じものでした。
Direct3D11 は Direct3D10 の GPU でも動作するそうです。
11 専用の機能を使用することは出来ませんが、9→10 よりはスムーズな移行が
できそうです。
でもこれは Direct3D9 以前は当たり前のことでした。
DirectX7 世代の GPU でもシェーダーなど未対応機能が走らないだけで DirectX8
が使えるし、ShaderModel1.x の Direct3D8 世代 GPU でも Direct3D9 がそのまま
使えます。
Direct3D9 から Direct3D10 への切り替えがあまりに特殊すぎました。
GPU も OS も新しくしなければならず互換性も無かったので。
関連エントリ
・Gamefest2008 と Direct3D 11
・AMD Stream SDK
・Gamefest 2008 Presentations
●Compute Shader
ポストフィルタなど Compute Shader の想定されている用途や、
RWTexture2D/RWBuffer が PixelShader でも使えることなどから、
Compute Shader は Pixel Shader に近いものだと考えられます。
実際に現在の GPU でも、AMD Stream SDK の CAL で走るプログラムは
PixelShader 相当だったため一致しています。
Compute Shader は 32KByte のシェアドメモリにアクセス可能です。
2048 float4 = 8192 float = 32KByte
共有メモリと言うよりも、スレッド間で共有されるレジスタ Shared Register と
表現されているようです。
RADEON HD4870 の CAL を使った global buffer (shared local) は実測で
2048 vector、ちょうど 32KByte でした。
これが Shared Memory (Register) 相当と考えられるためほぼ一致しています。
任意の読み書きと Shared Register 以外の特徴として Atomic 命令があります。
32bit 値の read-modify-write に対応しているようです。
命令も Windows のような Interlocked~()
これを利用してスレッド同期が可能となり、排他的なメモリへアクセスなどが
できるようです。
AMD CAL では見あたりませんでしたが、CUDA では 32/64bit の Atomic 命令を
備えているようです。
AMD CAL でも Global Buffer へのアクセス、たとえば
「add g[0].x, g[0].x, r1.x」が Atomic なら代用できますが
そうではないようです。
il_ps_2_0 dcl_input_position_interp(linear) v0.xy dcl_output_generic o0 dcl_literal l0, 1, 1, 0, 0 itof r1.x, l0.x add g[0].x, g[0].x, r1.x ret_dyn end
g[0].x は毎回異なる値となりました。
● API
Direct3D11 は Direct3D10 とほぼ同じ API セットを持ち、機能拡張されている
ものとなるそうです。
詳細は SDK のリリースを待たないといけませんが、おそらく Direct3D8 と
Direct3D9 の関係に近いものだと考えられます。
DirectX7 までのベースとなる DirectDraw を捨てて、Direct3D8 は作り直されました。
その後の Direct3D9 は完全に互換性があるとはいえないものの、考え方や設計は
Direct3D8 とほとんど同じものでした。
Direct3D11 は Direct3D10 の GPU でも動作するそうです。
11 専用の機能を使用することは出来ませんが、9→10 よりはスムーズな移行が
できそうです。
でもこれは Direct3D9 以前は当たり前のことでした。
DirectX7 世代の GPU でもシェーダーなど未対応機能が走らないだけで DirectX8
が使えるし、ShaderModel1.x の Direct3D8 世代 GPU でも Direct3D9 がそのまま
使えます。
Direct3D9 から Direct3D10 への切り替えがあまりに特殊すぎました。
GPU も OS も新しくしなければならず互換性も無かったので。
関連エントリ
・Gamefest2008 と Direct3D 11
・AMD Stream SDK
↓手書きのフォントもベクタ表示

続きです。
前回表示したアウトラインフォント のシェーダー側の動作となります。
入力データは quad のエッジ情報です。通常は VertexShader に頂点情報を渡しますが、
接続する 2頂点+2コントロールポイント座標を一組としてシェーダーに渡します。
1つの quad を描画するためには 4辺分+中心座標が必要なので、これが 5組あります。
よって描画データは下記の通り。
ラインの場合コントロールポイントが不要なので c0, c1 は使いません。
中心座標は x,y のみなので、残りは全部 0 です。
カーブの場合: v0 c0 c1 v1
ラインの場合: v0 -1e5 0 v1
IA に渡す頂点の定義は次の通り。float2 × 4
これが 5 個なので D3D11_PRIMITIVE_TOPOLOGY_5_CONTROL_POINT_PATCHLIST
で描画します。
Vertex Shader は素通り
Hull Shader はカーブかラインか区別して異なる TessFactor を設定します。
Doman Shader は生成された頂点がどのエッジか区別して、それぞれ異なるパラメータ
補間を適用します。
InsideTessFactor が 2.0 なので、Tessellator で分割されるのはエッジだけです。
上下左右のエッジは
・u が 0.0
・u が 1.0
・v が 0.0
・v が 1.0
で区別することができます。上のどれでもない場合は中心座標です。
例として 1辺だけ TessFactor[0] = 4 の場合を考えます。
このとき生成される頂点の uv は下記の 8点となります。
四隅
(0.0, 0.0)
(1.0, 0.0)
(0.0, 1.0)
(1.0, 1.0)
中心
(0.5, 0.5)
分割で挿入された点
(0.0, 0.25)
(0.0, 0.50)
(0.0, 0.75)
よって u が 0.0 の場合を区別すれば必要なエッジだけ取り出せることがわかります。
エッジの切り分けは、四隅の頂点が共有されている点に注意です。
どの uv 値をどのエッジに割り当てるのかは任意で構いませんが、四隅が共有されている
関係上、エッジの並びが連続していなければカーブが不連続になります。
またエッジ単位に Tess Factor のスケールを入れれば、長い辺など必要な場所のみ
より細かく分割するなどの応用ができるでしょう。
前回のアルゴリズムはまだ完全に形状を分割できるとは限らず、いくつか問題が生じる
ケースがあります。
(1) 分割時に対応する頂点が見つけられない場合がある
(2) アウトラインからはみ出さない位置に中心頂点を配置できない場合がある
このような状態に陥った場合、対象のプリミティブのエッジを分割して
頂点(アンカー)を追加することで対処可能です。
シェーダーを書き出していて気がつきましたが、これ GeometryShader だけでも実現
できそうですね。GeometryShader には 5 頂点入力がないので、頂点数さえ合わせれば
ハードウエアで実行できそうです。
関連エントリ
・Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
・Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver

続きです。
前回表示したアウトラインフォント のシェーダー側の動作となります。
入力データは quad のエッジ情報です。通常は VertexShader に頂点情報を渡しますが、
接続する 2頂点+2コントロールポイント座標を一組としてシェーダーに渡します。
1つの quad を描画するためには 4辺分+中心座標が必要なので、これが 5組あります。
よって描画データは下記の通り。
// 1 quad 分 v0 c0 c1 v1 v0 c0 c1 v1 v0 c0 c1 v1 v0 c0 c1 v1 g0 0 0 0 v0= x,y 頂点 c0= x,y コントロールポイント g0= x,y 中心座標 0= 0,0 未使用
ラインの場合コントロールポイントが不要なので c0, c1 は使いません。
中心座標は x,y のみなので、残りは全部 0 です。
カーブの場合: v0 c0 c1 v1
ラインの場合: v0 -1e5 0 v1
例 249.593597f,395.421600f, 302.652008f,346.582397f, 341.562408f,290.375214f, 366.312805f,226.812805f, 366.312805f,226.812805f, -1e5f,0, 0,0, 324.124786f,219.500000f, 324.124786f,219.500000f, 298.250397f,282.316803f, 263.750397f,333.031189f, 220.624802f,371.656006f, 220.624802f,371.656006f, -1e5f,0, 0,0, 249.593597f,395.421600f, 293.008575f,305.777374f, 0,0, 0,0, 0,0,
IA に渡す頂点の定義は次の通り。float2 × 4
D3D11_INPUT_ELEMENT_DESC ldesc[]= { { "VPOS", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "VPOS", 1, DXGI_FORMAT_R32G32_FLOAT, 0, sizeof(float)*2, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "VPOS", 2, DXGI_FORMAT_R32G32_FLOAT, 0, sizeof(float)*4, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "VPOS", 3, DXGI_FORMAT_R32G32_FLOAT, 0, sizeof(float)*6, D3D11_INPUT_PER_VERTEX_DATA, 0 }, };
これが 5 個なので D3D11_PRIMITIVE_TOPOLOGY_5_CONTROL_POINT_PATCHLIST
で描画します。
Vertex Shader は素通り
struct VS_INPUT { float2 vPos0 : VPOS0; float2 vC0 : VPOS1; float2 vC1 : VPOS2; float2 vPos1 : VPOS3; }; struct VS_OUTPUT { float2 vPos0 : VSPOS0; float2 vC0 : VSPOS1; float2 vC1 : VSPOS2; float2 vPos1 : VSPOS3; }; VS_OUTPUT main( VS_INPUT idata ) { VS_OUTPUT odata; odata.vPos0= idata.vPos0; odata.vPos1= idata.vPos1; odata.vC0= idata.vC0; odata.vC1= idata.vC1; return odata; }
Hull Shader はカーブかラインか区別して異なる TessFactor を設定します。
#define MAX_POINTS 5 struct VS_OUTPUT { float2 vPos0 : VSPOS0; float2 vC0 : VSPOS1; float2 vC1 : VSPOS2; float2 vPos1 : VSPOS3; }; struct HS_OUTPUT { float2 vPos0 : HSPOS0; float2 vC0 : HSPOS1; float2 vC1 : HSPOS2; float2 vPos1 : HSPOS3; }; struct HS_DATA { float Edge[4] : SV_TessFactor; float Inside[2] : SV_InsideTessFactor; }; HS_DATA func( InputPatch<VS_OUTPUT,MAX_POINTS> ip ) { HS_DATA pdata; const float tf= 8.0; const float lineFlag= -1e4; // ラインを区別する pdata.Edge[3]= ip[0].vC0.x > lineFlag ? tf : 1.0f; pdata.Edge[2]= ip[1].vC0.x > lineFlag ? tf : 1.0f; pdata.Edge[1]= ip[2].vC0.x > lineFlag ? tf : 1.0f; pdata.Edge[0]= ip[3].vC0.x > lineFlag ? tf : 1.0f; // 内部は 2固定 pdata.Inside[0]= 2.0; pdata.Inside[1]= 2.0; return pdata; } [domain("quad")] [partitioning("integer")] [outputtopology("triangle_cw")] [outputcontrolpoints(5)] [patchconstantfunc("func")] HS_OUTPUT main( InputPatch<VS_OUTPUT,MAX_POINTS> ip, uint id : SV_OutputControlPointID ) { HS_OUTPUT odata; odata.vPos0= ip[id].vPos0; odata.vPos1= ip[id].vPos1; odata.vC0= ip[id].vC0; odata.vC1= ip[id].vC1; return odata; }
Doman Shader は生成された頂点がどのエッジか区別して、それぞれ異なるパラメータ
補間を適用します。
InsideTessFactor が 2.0 なので、Tessellator で分割されるのはエッジだけです。
上下左右のエッジは
・u が 0.0
・u が 1.0
・v が 0.0
・v が 1.0
で区別することができます。上のどれでもない場合は中心座標です。
例として 1辺だけ TessFactor[0] = 4 の場合を考えます。
このとき生成される頂点の uv は下記の 8点となります。
四隅
(0.0, 0.0)
(1.0, 0.0)
(0.0, 1.0)
(1.0, 1.0)
中心
(0.5, 0.5)
分割で挿入された点
(0.0, 0.25)
(0.0, 0.50)
(0.0, 0.75)
よって u が 0.0 の場合を区別すれば必要なエッジだけ取り出せることがわかります。
#define CONTROL_POINT 5 struct HS_DATA { float Edge[4] : SV_TessFactor; float Inside[2] : SV_InsideTessFactor; }; struct HS_OUTPUT { float2 vPos0 : HSPOS0; float2 vC0 : HSPOS1; float2 vC1 : HSPOS2; float2 vPos1 : HSPOS3; }; struct DS_OUTPUT { float4 vPos : SV_Position; }; cbuffer pf { float4x4 WVP; }; // カーブの場合 float2 UVtoPositionB( const HS_OUTPUT p, float t ) { float t2= 1.0-t; float4 u= float4( t2*t2*t2, t2*t2* t*3, t2* t* t*3, t* t* t ); return u.x*p.vPos0.xy +u.y*p.vC0.xy +u.z*p.vC1.xy +u.w*p.vPos1.xy; } // 直線の場合 float2 UVtoPositionL( const HS_OUTPUT p, float ux ) { return p.vPos0 * (1-ux) + p.vPos1 * ux; // 実際は不要 } float2 UVtoPosition( const HS_OUTPUT p, float ux ) { if( p.vC0.x < -1e4 ){ return UVtoPositionL( p, ux ); } return UVtoPositionB( p, ux ); } [domain("quad")] DS_OUTPUT main( HS_DATA ip, float2 uv : SV_DomainLocation, const OutputPatch<HS_OUTPUT,CONTROL_POINT> bpatch ) { DS_OUTPUT dout; const float EPC= 0.0f; // 誤差が問題になるなら増やす float2 pos= float2( 0, 0 ); // 各エッジを切り分ける if( uv.y >= 1.0-EPC ){ pos= UVtoPosition( bpatch[0], uv.x ); }else if( uv.y <= 0.0+EPC ){ pos= UVtoPosition( bpatch[2], 1-uv.x ); }else if( uv.x >= 1.0-EPC ){ pos= UVtoPosition( bpatch[1], 1-uv.y ); }else if( uv.x <= 0.0+EPC ){ pos= UVtoPosition( bpatch[3], uv.y ); }else{ pos= bpatch[4].vPos0.xy; // 中心 } dout.vPos= mul( float4( pos.xy, 0, 1 ), WVP ); return dout; }
エッジの切り分けは、四隅の頂点が共有されている点に注意です。
どの uv 値をどのエッジに割り当てるのかは任意で構いませんが、四隅が共有されている
関係上、エッジの並びが連続していなければカーブが不連続になります。
またエッジ単位に Tess Factor のスケールを入れれば、長い辺など必要な場所のみ
より細かく分割するなどの応用ができるでしょう。
前回のアルゴリズムはまだ完全に形状を分割できるとは限らず、いくつか問題が生じる
ケースがあります。
(1) 分割時に対応する頂点が見つけられない場合がある
(2) アウトラインからはみ出さない位置に中心頂点を配置できない場合がある
このような状態に陥った場合、対象のプリミティブのエッジを分割して
頂点(アンカー)を追加することで対処可能です。
シェーダーを書き出していて気がつきましたが、これ GeometryShader だけでも実現
できそうですね。GeometryShader には 5 頂点入力がないので、頂点数さえ合わせれば
ハードウエアで実行できそうです。
関連エントリ
・Direct3D11/DirectX11 (14) GPU を使ったアウトラインフォントの描画の(2)
・Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
・Direct3D11/DirectX11 (12) テセレータのレンダーステート他
・Direct3D11/DirectX11 (11) 互換性とシェーダーの対応表など
・Direct3D11/DirectX11 (10) テセレータの補間
・Direct3D11/DirectX11 (9) テセレータによるアウトラインフォントの描画など
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2009/02/02
Direct2D と Direct3D10.1 の下位互換
● Direct2D とは
Direct3D に似ていますが別物です。
DirectDraw とも似ていますが役割が異なります。
現在 DirectX SDK ではなく Windows SDK for Windows 7 の方に含まれます。
下記のページより Windows7 SDK の beta 版を入手することが可能です。
・Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA
Direct3D11 と違い WindowsVista では動かないので、動作確認には Windows7 beta が
必要となります。ドキュメントはこちらから。
・MSDN Direct2D
Direct2D は Direct3D よりも上位のレイヤに位置します。
レンダーバックエンドとして Direct3D を活用しつつ、ベクターや bitmap 等の 2D
レンダリングを可能とします。Direct3D や DirectDraw のようなハードウエア寄りの
プリミティブではなく、GDI のような高度な描画命令を多数有しています。
SVG/FXG/XAML のようなベクターグラフィックスに対応し、ポリゴンを表示するかわり
に美しい 2D をレンダリングすることに注力した API セットであるといえるでしょう。
現在の Direct2D が利用するのは Direct3D10.1 です。
10.1 といえば WARP によって高速なソフトウエアラスタライズも可能だし、リモート
デスクトップ等で利用可能なCommand Remoting だって使えます。
Direct2D の狙いはまさにここにあります。
ハードウエアアクセラレーション、ソフトウエアラスタライザ、そしてクライアント
サイドのレンダリングなど、2D の描画でもこれらの恩恵を受けられるようになります。
● Direct2D の謎
最初に疑問に思うのは Direct3D10.1 だと使えるハードがかなり限られてくるのでは
ないかということ。
従来 Direct3D10 は専用のビデオカードしか使えず D3D9 世代の GPU とは互換性が
ありませんでした。逆に Direct3D11 の方が下位互換性が強化されており、D3D9 世代の
GPU でも動作します。そのあたりをまとめたのは こちら。
どうやら D3D10 でも下位互換がサポートされることになった模様です。
たとえば一番新しい Direct3D SDK November 2008 のヘッダファイル D3D10_1.h を
見ても、下記の通り FEATURE LEVEL には 10 世代の GPU しか記載されていません。
ところが Windows7 SDK beta 付属の D3D10_1.h を見てみると・・
いつの間にか下位の FEATURE LEVEL が追加されています。
我慢を強いられた Direct3D10/Vista 世代はいったい何だったのでしょうか。
つまり Direct2D は Direct3D9 世代の古い GPU でもハードウエアアクセラレーション
がかかります。(実際に試してみました→後述)
さらに Direct2D は対応ハードウエアがなくてもソフトウエアラスタライザによって
動作可能と書かれています。これが WARP を指しているのか、それとも Direct2D が
さらに独自でラスタライザを有しているのかわかりません。
ハードウエアアクセラレーションといいつつも、おそらくジオメトリ部分は CPU の
割合がそれなりに高いのではないかと思われます。アンチエリアス等のピクセル合成
部分において GPU が活用されているのではないでしょうか。
Direct3D11 世代になればジオメトリ処理でも GPU の割合が高くなるかもしれません。
以前アウトラインフォントの GPU 描画に取り組んだのはジオメトリも GPU で処理させる
ことが目的でした。
もう一つ疑問に思ったのは Direct2D のインターフェースが最初から D2D1 と "1" が
ついていること。DXGI1 など他の API と同調するためなのか、"2" の登場
(Direct3D11 版?) を想定しているのかわかりません。
DirectWrite には "1" がついていませんでした。
●下位互換性の確認
3台とも Windows7 beta
Aero 無効かつ D3D10/11 全滅の GMA500 では HARDWARE_RENDERING だと
動きません。SOFTWARE_RENDERING では動作しました。
このことから HARDWARE/SOFTWARE のフラグ設定は機能しているものと思われます。
GMA950 の EeePC では両方動作しているので、ShaderModel2.0 でも Direct2D で
ハードウエアアクセラレーションが有効になっているのではないかと考えられます。
つまり Windows7 では Direct3D10.1 で ShaderModel2~3 が動いているということ。
直接 Direct3D10.1 を試した方が早かったかもしれません。
関連エントリ
・Intel GMA500 のスペックについて考える。続き (2)
・Windows7 リモートデスクトップと Direct3D
・Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
Direct3D に似ていますが別物です。
DirectDraw とも似ていますが役割が異なります。
現在 DirectX SDK ではなく Windows SDK for Windows 7 の方に含まれます。
下記のページより Windows7 SDK の beta 版を入手することが可能です。
・Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA
Direct3D11 と違い WindowsVista では動かないので、動作確認には Windows7 beta が
必要となります。ドキュメントはこちらから。
・MSDN Direct2D
Direct2D は Direct3D よりも上位のレイヤに位置します。
レンダーバックエンドとして Direct3D を活用しつつ、ベクターや bitmap 等の 2D
レンダリングを可能とします。Direct3D や DirectDraw のようなハードウエア寄りの
プリミティブではなく、GDI のような高度な描画命令を多数有しています。
SVG/FXG/XAML のようなベクターグラフィックスに対応し、ポリゴンを表示するかわり
に美しい 2D をレンダリングすることに注力した API セットであるといえるでしょう。
現在の Direct2D が利用するのは Direct3D10.1 です。
10.1 といえば WARP によって高速なソフトウエアラスタライズも可能だし、リモート
デスクトップ等で利用可能なCommand Remoting だって使えます。
Direct2D の狙いはまさにここにあります。
ハードウエアアクセラレーション、ソフトウエアラスタライザ、そしてクライアント
サイドのレンダリングなど、2D の描画でもこれらの恩恵を受けられるようになります。
● Direct2D の謎
最初に疑問に思うのは Direct3D10.1 だと使えるハードがかなり限られてくるのでは
ないかということ。
従来 Direct3D10 は専用のビデオカードしか使えず D3D9 世代の GPU とは互換性が
ありませんでした。逆に Direct3D11 の方が下位互換性が強化されており、D3D9 世代の
GPU でも動作します。そのあたりをまとめたのは こちら。
どうやら D3D10 でも下位互換がサポートされることになった模様です。
たとえば一番新しい Direct3D SDK November 2008 のヘッダファイル D3D10_1.h を
見ても、下記の通り FEATURE LEVEL には 10 世代の GPU しか記載されていません。
typedef enum D3D10_FEATURE_LEVEL1 { D3D10_FEATURE_LEVEL_10_0 = 0xa000, D3D10_FEATURE_LEVEL_10_1 = 0xa100 } D3D10_FEATURE_LEVEL1;
ところが Windows7 SDK beta 付属の D3D10_1.h を見てみると・・
typedef enum D3D10_FEATURE_LEVEL1 { D3D10_FEATURE_LEVEL_10_0 = 0xa000, D3D10_FEATURE_LEVEL_10_1 = 0xa100, D3D10_FEATURE_LEVEL_9_1 = 0x9100, D3D10_FEATURE_LEVEL_9_2 = 0x9200, D3D10_FEATURE_LEVEL_9_3 = 0x9300 } D3D10_FEATURE_LEVEL1;
いつの間にか下位の FEATURE LEVEL が追加されています。
我慢を強いられた Direct3D10/Vista 世代はいったい何だったのでしょうか。
つまり Direct2D は Direct3D9 世代の古い GPU でもハードウエアアクセラレーション
がかかります。(実際に試してみました→後述)
さらに Direct2D は対応ハードウエアがなくてもソフトウエアラスタライザによって
動作可能と書かれています。これが WARP を指しているのか、それとも Direct2D が
さらに独自でラスタライザを有しているのかわかりません。
ハードウエアアクセラレーションといいつつも、おそらくジオメトリ部分は CPU の
割合がそれなりに高いのではないかと思われます。アンチエリアス等のピクセル合成
部分において GPU が活用されているのではないでしょうか。
Direct3D11 世代になればジオメトリ処理でも GPU の割合が高くなるかもしれません。
以前アウトラインフォントの GPU 描画に取り組んだのはジオメトリも GPU で処理させる
ことが目的でした。
もう一つ疑問に思ったのは Direct2D のインターフェースが最初から D2D1 と "1" が
ついていること。DXGI1 など他の API と同調するためなのか、"2" の登場
(Direct3D11 版?) を想定しているのかわかりません。
DirectWrite には "1" がついていませんでした。
●下位互換性の確認
3台とも Windows7 beta
・DesktopPC RADEON HD4850 (Direct3D10.1 ShaderModel4.1) (Aero 有効) ○ D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING ・EeePC901 945GE GMA950 (Direct3D9 ShaderModel2.0) (Aero 有効) ○ D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING ・VAIO type P US15W GMA500 (Direct3D9 ShaderModel3.0) (Aero 無効) × D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING
Aero 無効かつ D3D10/11 全滅の GMA500 では HARDWARE_RENDERING だと
動きません。SOFTWARE_RENDERING では動作しました。
このことから HARDWARE/SOFTWARE のフラグ設定は機能しているものと思われます。
GMA950 の EeePC では両方動作しているので、ShaderModel2.0 でも Direct2D で
ハードウエアアクセラレーションが有効になっているのではないかと考えられます。
つまり Windows7 では Direct3D10.1 で ShaderModel2~3 が動いているということ。
直接 Direct3D10.1 を試した方が早かったかもしれません。
関連エントリ
・Intel GMA500 のスペックについて考える。続き (2)
・Windows7 リモートデスクトップと Direct3D
・Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
DirectX SDK November 2008 Technical Preview 4回目です。
●DirectX SDK November 2008 の隠れた変更 (D3D10)
以前作成した D3D10 用プログラムのコンパイルが通らなくなったため調べたところ
今回から D3DX10DisassembleEffect() が無くなってるようです。
Compiler 改良のため core API でなく D3DX になったのは良いのですが
仕様が変わるのは困ります。
D3D11 では HLSL コンパイラは D3DCompiler.h に統合され、バージョンに関係ない
共通 API に進化しているようです。blob 等の API は D3D10 のまま。
こちらの方に D3DX10DisassembleEffect() の後継らしき
D3DDisassemble10Effect()
がありました。でも D3DCompiler.h は d3d11shader.h を include しているので
D3D10 ではコンパイルが通りませんでした。
●FeatureLevel 一覧
Driver (device type) 毎の FeatureLevel を調べてみました。
予想通り、D3D_DRIVER_TYPE_HARDWARE の場合のみ異なっています。
Reference と Null Reference はどちらも 11.0 です。
WARP は現在 10.1 までの対応でした。
上記よりわかるのは GeForce でも WARP を使えば 10.1 のテストが出来ること。
また 11.0 の動作を確認するには現状 Reference を使うしかないようです。
それにしても D3D11 になって、テスト用に GeForce7900 を復活させることに
なるとは思いませんでした。
付属 Direct3D11 サンプルの MultithreadedRendering11 は GeForce7900GTX でも
動作しました。(Direct3D10 はいったい何だったのか)
ちなみにサンプルは WARP に対応していないので、FeatureLevel が合わない場合
強制的に Reference Driver になります。
これは DXUT の問題なので、WARP に対応させる場合は DXUT を書き換える必要が
あります。
EeePC 901 (Vista) の Intel 945 (ShaderModel2.0) もなぜか FeatureLevel 9.3 を
返します。MultithreadedRendering11 は動作しませんでした。
D3D11_FEATURE_THREADING が未サポートなためかもしれません。
● D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS
マニュアルに載ってない FEATURE オプションとして
D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS があるようです。
これは D3D10 デバイス (FeatureLevel 4.0 or 4.1) でも、FeatureLevel 11.0 相当の
機能を一部有している場合に用いられるようです。
前回 fxc のプロファイルで ComputeShader 4.0 , 4.1 が存在していることを指摘
しました。D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS はまさに、D3D10 デバイス
でも ComputeShader が使えるかどうか調べるために用いられます。
現在 D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS のメンバは
ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
だけです。
10.1 デバイスである WARP も FALSE でした。
よって今現在 ComputeShader を試すには Reference しか無いようです。
その他調べた FueatureSupport は下記の通り。
Reference と Null Reference で CheckFeatureSupport() の結果が異なるのが
気になります。cpas とどこが違うんだろうという気にならなくもないです。
10.0 / 10.1 ハードウエアも、将来ドライバの更新でこれらの対応状況が変化すると
思われます。
●新テクスチャ形式
マニュアルには記載されてませんが、D3D11 の新しいテクスチャ形式はすでに
DXGIFormat.h に追加されています。
増えたフォーマットは下記の通り。
lib に dxgi_beta.lib が存在しているので、おそらく dxgi.lib の代わりに
こちらをリンクすれば新フォーマットが使えそうです。
help 内の扱いが変わったことから予想してましたが、やはり DXGI は共通のままで
(DXGI2 のような) 新インターフェースに分岐しませんでした。
もしかしたら D3D10 でも使えるようになるのかもしれません。
追加された B8G8R8A8 (ARGB) 系は以前からある
DXGI_FORMAT_B8G8R8A8_UNORM = 87 の追加バリエーションとなります。
これ D3D10 の一般的なフォーマット R8G8B8A8 (ABGR) と逆順です。
Direct3D9 以前ではこっちの方が標準でした。
(D3D関連 DDSテクスチャの取り扱い)
仕様として隅に追いやられてたはずのフォーマットを正式な扱いに戻したのは、
やはり D3D9 ShaderModel3.0 以前の GPU に対応するために、必要だったからでは
ないでしょうか。
BC6/BC7 が新しい圧縮テクスチャ形式です。
詳細はまだわかりませんが、F16 が使われている BC6 の方が HDR 圧縮形式だと
考えられます。
Preview (beta) とはいえ、1つの SDK に 3世代のバージョンが同居しているのは
Direct3D で初めてです。
DXGI は D3D10 でも D3D11 でも共通に用いられますし、HLSL コンパイラは全世代で
共有されています。
アップデートの単位が徐々に細分化しているため、バージョンの区切りはこれから
ますます曖昧になっていくかもしれません。
関連エントリ
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
・Direct3D11 シェーダーの動的リンクやテクスチャ
・Direct3D11 マルチスレッドのための機能
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
●DirectX SDK November 2008 の隠れた変更 (D3D10)
以前作成した D3D10 用プログラムのコンパイルが通らなくなったため調べたところ
今回から D3DX10DisassembleEffect() が無くなってるようです。
Compiler 改良のため core API でなく D3DX になったのは良いのですが
仕様が変わるのは困ります。
D3D11 では HLSL コンパイラは D3DCompiler.h に統合され、バージョンに関係ない
共通 API に進化しているようです。blob 等の API は D3D10 のまま。
こちらの方に D3DX10DisassembleEffect() の後継らしき
D3DDisassemble10Effect()
がありました。でも D3DCompiler.h は d3d11shader.h を include しているので
D3D10 ではコンパイルが通りませんでした。
●FeatureLevel 一覧
Driver (device type) 毎の FeatureLevel を調べてみました。
・RADEON HD4850 (8.10) HARDWARE = 10.1 (a100) REFERENCE = 11.0 (b000) NULL = 11.0 (b000) WARP = 10.1 (a100) ・GeForce GTX260 (178.24) HARDWARE = 10.0 (a000) REFERENCE = 11.0 (b000) NULL = 11.0 (b000) WARP = 10.1 (a100) ・GeForce 7900GTX (178.24) HARDWARE = 9.3 (9300) REFERENCE = 11.0 (b000) NULL = 11.0 (b000) WARP = 10.1 (a100) ・Intel 945/GMA950 (7.14.10.1461) HARDWARE = 9.3 (9300) REFERENCE = 11.0 (b000) NULL = 11.0 (b000) WARP = 10.1 (a100)
予想通り、D3D_DRIVER_TYPE_HARDWARE の場合のみ異なっています。
Reference と Null Reference はどちらも 11.0 です。
WARP は現在 10.1 までの対応でした。
上記よりわかるのは GeForce でも WARP を使えば 10.1 のテストが出来ること。
また 11.0 の動作を確認するには現状 Reference を使うしかないようです。
それにしても D3D11 になって、テスト用に GeForce7900 を復活させることに
なるとは思いませんでした。
付属 Direct3D11 サンプルの MultithreadedRendering11 は GeForce7900GTX でも
動作しました。(Direct3D10 はいったい何だったのか)
ちなみにサンプルは WARP に対応していないので、FeatureLevel が合わない場合
強制的に Reference Driver になります。
これは DXUT の問題なので、WARP に対応させる場合は DXUT を書き換える必要が
あります。
EeePC 901 (Vista) の Intel 945 (ShaderModel2.0) もなぜか FeatureLevel 9.3 を
返します。MultithreadedRendering11 は動作しませんでした。
D3D11_FEATURE_THREADING が未サポートなためかもしれません。
● D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS
マニュアルに載ってない FEATURE オプションとして
D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS があるようです。
これは D3D10 デバイス (FeatureLevel 4.0 or 4.1) でも、FeatureLevel 11.0 相当の
機能を一部有している場合に用いられるようです。
前回 fxc のプロファイルで ComputeShader 4.0 , 4.1 が存在していることを指摘
しました。D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS はまさに、D3D10 デバイス
でも ComputeShader が使えるかどうか調べるために用いられます。
現在 D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS のメンバは
ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
だけです。
10.1 デバイスである WARP も FALSE でした。
よって今現在 ComputeShader を試すには Reference しか無いようです。
その他調べた FueatureSupport は下記の通り。
Reference と Null Reference で CheckFeatureSupport() の結果が異なるのが
気になります。cpas とどこが違うんだろうという気にならなくもないです。
REFERENCE 11.0 thread DriverConcurrentCreates= 0 thread DriverCommandLists= 0 double DoublePrecisionFloatShaderOps= 0 ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x= 1 NULL REFERENCE 11.0 thread DriverConcurrentCreates= 1 thread DriverCommandLists= 1 double DoublePrecisionFloatShaderOps= 1 ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x= 1 WARP 10.1 thread DriverConcurrentCreates= 0 thread DriverCommandLists= 0 double DoublePrecisionFloatShaderOps= 0 ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x= 0 HARDWARE 9.3 (GeForce7900GTX) thread DriverConcurrentCreates= 0 thread DriverCommandLists= 0 double DoublePrecisionFloatShaderOps= 0 ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x= 0 HARDWARE 10.1 (RADEON HD4850) thread DriverConcurrentCreates= 0 thread DriverCommandLists= 0 double DoublePrecisionFloatShaderOps= 0 ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x= 0
10.0 / 10.1 ハードウエアも、将来ドライバの更新でこれらの対応状況が変化すると
思われます。
●新テクスチャ形式
マニュアルには記載されてませんが、D3D11 の新しいテクスチャ形式はすでに
DXGIFormat.h に追加されています。
増えたフォーマットは下記の通り。
DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, DXGI_FORMAT_BC6H_TYPELESS = 94, DXGI_FORMAT_BC6H_UF16 = 95, DXGI_FORMAT_BC6H_SF16 = 96, DXGI_FORMAT_BC7_TYPELESS = 97, DXGI_FORMAT_BC7_UNORM = 98, DXGI_FORMAT_BC7_UNORM_SRGB = 99,
lib に dxgi_beta.lib が存在しているので、おそらく dxgi.lib の代わりに
こちらをリンクすれば新フォーマットが使えそうです。
help 内の扱いが変わったことから予想してましたが、やはり DXGI は共通のままで
(DXGI2 のような) 新インターフェースに分岐しませんでした。
もしかしたら D3D10 でも使えるようになるのかもしれません。
追加された B8G8R8A8 (ARGB) 系は以前からある
DXGI_FORMAT_B8G8R8A8_UNORM = 87 の追加バリエーションとなります。
これ D3D10 の一般的なフォーマット R8G8B8A8 (ABGR) と逆順です。
Direct3D9 以前ではこっちの方が標準でした。
(D3D関連 DDSテクスチャの取り扱い)
仕様として隅に追いやられてたはずのフォーマットを正式な扱いに戻したのは、
やはり D3D9 ShaderModel3.0 以前の GPU に対応するために、必要だったからでは
ないでしょうか。
BC6/BC7 が新しい圧縮テクスチャ形式です。
詳細はまだわかりませんが、F16 が使われている BC6 の方が HDR 圧縮形式だと
考えられます。
Preview (beta) とはいえ、1つの SDK に 3世代のバージョンが同居しているのは
Direct3D で初めてです。
DXGI は D3D10 でも D3D11 でも共通に用いられますし、HLSL コンパイラは全世代で
共有されています。
アップデートの単位が徐々に細分化しているため、バージョンの区切りはこれから
ますます曖昧になっていくかもしれません。
関連エントリ
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
・Direct3D11 シェーダーの動的リンクやテクスチャ
・Direct3D11 マルチスレッドのための機能
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
2008/11/06
DirectX SDK November 2008
リリースされたようです。
DirectX SDK November 2008
・DirectX Software Development Kit
予告通り Direct3D11 の Technical Preview が含まれています。
DirectX SDK November 2008
・DirectX Software Development Kit
予告通り Direct3D11 の Technical Preview が含まれています。
2008/09/20
Direct3D11 のパイプライン
今日も Gamefest2008 の資料を参考に、D3D11 関連をいろいろ見てみます。
・Gamefest 2008 Presentations
D3D11 ではテセレータと共に新しくシェーダーステージが追加されています。
Direct3D 11 の目玉機能の一つです。
新しく Patch プリミティブが導入されるようです。
Quad で指定可能で、かつ隣接する頂点情報を入力できます。
D3D10 GeometryShader の Triangle Adjacency に少々似てますが、
Triangle Adj は最大6頂点まででした。
Patch は最大 32 頂点まで入力できるようです。
たとえば単純で均等な Quad mesh なら、それぞれの隣接 Quad を1つ受け取ると
12頂点です。
これをコントロールポイントとして使います。
GeometryShader ではプリミティブ毎に 1つのシェーダーが走る形式でした。
HullShader は周囲の頂点情報を受け取りつつも、各入力頂点 (ControlPoint)
毎にシェーダー (Thread) を走らせることが可能となるようです。
頂点シェーダーと違うのは、それぞれのシェーダーが同じパッチ内のすべての
コントロールポイントにアクセス出来るということ。
また HullShader は、GS のようにパッチ毎に 1つだけ走らせることもできそうです。
おそらく、パッチ単位で走る HullShader 関数と、コントロールポイント毎に
走る HullShader 関数の両方を指定出来るのではないかと思われます。
HullShader はテセレータの前段階です。
テセレータの後に位置するのが DomainShader と呼ばれます。
DomainShader はテセレータで生成された頂点毎にシェーダーが走るようです。
この場合もパッチ単位で、入力されたコントロールポイントすべての情報を
受け取ることができます。
このように細切れで ShaderStage が増えているのは、入出力情報のグループ化に
よるものでしょうか。
とりあえずまとめてみました
関連エントリ
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
・Gamefest 2008 Presentations
D3D11 ではテセレータと共に新しくシェーダーステージが追加されています。
Direct3D 11 の目玉機能の一つです。
新しく Patch プリミティブが導入されるようです。
Quad で指定可能で、かつ隣接する頂点情報を入力できます。
D3D10 GeometryShader の Triangle Adjacency に少々似てますが、
Triangle Adj は最大6頂点まででした。
Patch は最大 32 頂点まで入力できるようです。
たとえば単純で均等な Quad mesh なら、それぞれの隣接 Quad を1つ受け取ると
12頂点です。
これをコントロールポイントとして使います。
GeometryShader ではプリミティブ毎に 1つのシェーダーが走る形式でした。
HullShader は周囲の頂点情報を受け取りつつも、各入力頂点 (ControlPoint)
毎にシェーダー (Thread) を走らせることが可能となるようです。
頂点シェーダーと違うのは、それぞれのシェーダーが同じパッチ内のすべての
コントロールポイントにアクセス出来るということ。
また HullShader は、GS のようにパッチ毎に 1つだけ走らせることもできそうです。
おそらく、パッチ単位で走る HullShader 関数と、コントロールポイント毎に
走る HullShader 関数の両方を指定出来るのではないかと思われます。
HullShader はテセレータの前段階です。
テセレータの後に位置するのが DomainShader と呼ばれます。
DomainShader はテセレータで生成された頂点毎にシェーダーが走るようです。
この場合もパッチ単位で、入力されたコントロールポイントすべての情報を
受け取ることができます。
このように細切れで ShaderStage が増えているのは、入出力情報のグループ化に
よるものでしょうか。
とりあえずまとめてみました
VertexShader 頂点毎に走る。 入力情報はその1頂点のみ。 HullShader パッチ毎。 パッチのコントロールポイントを全部参照できる。 HullShader コントロールポイント毎。パッチのコントロールポイントを全部参照できる。 DomainShader 生成された頂点毎。 パッチの入力コントロールポイントを全部参照できる。 GeometryShader 生成されたプリミティブ毎 。プリミティブを構成する頂点を全部参照できる。 テセレータがプリミティブとして Adjacency を生成できるかどうかは不明。 PixelShader ラスタライズされたピクセル毎。GS or VS 出力から補間された値が入力となる。
関連エントリ
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
DirectX SDK November 2008 Technical Preview 5回目です。
WARP の試し方の補足と、D3D11 の大きな目玉のひとつである
「動的なシェーダーリンク」について。
● Direct3D 11 が Direct3D 10 より多くのハードで動く理由
(1) Direct3D 9 世代の GPU に対応した
(2) ソフトレンダラ WARP が追加された
(1) ShaderModel 2.0~3.0 でも API 自体は動きます。
ただし固定パイプライン用機能が存在しないため Shader 必須。
D3D11 になったからといって GPU の能力を超えて出来ることが増える訳じゃないので、
使えない機能多数。逆に使用できないステートなど制限が生じる可能性あり。
(2) GPU に必要な 3D 機能が無くても WARP を使えば CPU だけで動作します。
DirectX SDK November 2008 現在 10.1 相当で、Reference Driver よりずっと高速。
●手っ取り早く WARP を試す方法
install したサンプルの DXUT.cpp 2907行あたりに
pDeviceSettings->d3d11.DriverType= D3D_DRIVER_TYPE_WARP;
を挿入します。下記の赤い行。WARP Driver で起動するようになります。
起動直後 = WARP
オプション画面から REFERENCE or HARDWARE に切り替え可能。(WARP には戻せない)
WARP device も FeatureLevel 10.1 なので、10.1 で動かないサンプルは動きません。
DirectX SDK November 2008 現在、動くのは MultithreadedRendering11 だけです。
実行速度は CPU 速度に依存します。
● Dynamic Shader Linkage
Direct3D 11 の大きな特徴の一つがこの Dynamic Shader Linkage です。
動的なリンクというと、複数のシェーダーバイナリをリンクし相互参照を解決する
イメージがありますが・・違います。
わかりやすく言えば「シェーダー内で関数ポインタが使えるようになった」ということ。
インスタンス自体は静的に生成されており、単一のシェーダーバイナリにすべての
インスタンス及びエントリポイントが含まれていなければなりません。
一つのシェーダープログラムの中に複数の関数を作成することが出来、その飛び先を
動的に切換えることができるわけです。
これらの仕組みは HLSL 上では class と interface の形で用いられます。
上の例では Green と Red が interface Base を継承して作られています。
実行は Base で宣言された color を経由して行われており、どのメソッドが
呼び出されるのかコンパイル時にはわかりません。
Green 及び Red のインスタンスは cbRed, cbGreen として ConstantBuffer 上に
静的に作られています。外部の C++ 側からは、このインスタンス化された "cbRed"、
"cbGreen" を参照することが出来ます。
実際にどのインスタンスを color に割り当てるのか、レンダリング時に C++ 側で
いつでも切換えられるわけです。
シェーダー内のインスタンス参照には ID3D11ClassLinkage::GetClassInstance()
を使います。
ID3D11DeviceContext::PSSetShader() で、シェーダーと同時に使用する
インスタンスのリストを渡します。
Shader 内には複数の interface が宣言されている可能性があるので、必ずしも
上のように単純になりません。Reflection を参照して、PSSetShader() に渡す
インスタンスリストの何番目がどの interface 呼び出しに対応するのか調べる
必要があります。
class に変数が含まれている場合、ConstantBuffer 内のメモリ配置との対応付けも
必要です。(この場合 Effect ではないのでバッファの作成も必要)
Dynamic Shader Linkage の仕組みは、このようにパフォーマンスに影響が出ないよう
慎重に作られているため扱いは必ずしも簡単ではないようです。
今後 Direct3D11 対応の Effect fx_5_0 が完成したらある程度自動で行ってくれる
ようになるかもしれません。
出力されたコードを見ると fcall 命令が使われています。label の値 (おそらく
オフセット) を用いてサブルーチン呼び出しを行う仕組みだと考えられます。
上記のように飛び先と種類はコンパイル時に決定しており、おそらく外部参照はできません。
このことは次の例を見てもわかります。
この場合 interface を利用しつつも、継承されたエントリが Blue しかありません。
驚くことに HLSL コンパイラは Blue 以外が決して呼ばれないことを認識し、
間接呼び出しを取り除きます。
つまり main の中には Blue::GetColor() がインライン展開されてしまいます。
class の宣言自体は ShaderModel 4.1 (FeatureLevel 10.1) 以前でも使えます。
ただし間接呼び出しは出来ないので、interface からの呼び出しはエラーとなります。
上の例だと「Base color;」を「Blue color;」に書き換えればコンパイル可能です。
ちなみに従来の 4.1 以前のシェーダーにもサブルーチン呼び出しの機能自体は存在
していました。しかしながら HLSL コンパイラはすべてをインライン展開し尽くすので
実際に使われることはほとんどありません。
自分が知る限り、実際に関数呼び出しのコードが出力されるのは switch 文の
attribute に [call] を指定した場合だけでした。
Direct3D11 になってようやくシェーダー内のサブルーチン呼び出しが意味を持つ
ようになりました。
関連エントリ
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
WARP の試し方の補足と、D3D11 の大きな目玉のひとつである
「動的なシェーダーリンク」について。
● Direct3D 11 が Direct3D 10 より多くのハードで動く理由
(1) Direct3D 9 世代の GPU に対応した
(2) ソフトレンダラ WARP が追加された
(1) ShaderModel 2.0~3.0 でも API 自体は動きます。
ただし固定パイプライン用機能が存在しないため Shader 必須。
D3D11 になったからといって GPU の能力を超えて出来ることが増える訳じゃないので、
使えない機能多数。逆に使用できないステートなど制限が生じる可能性あり。
(2) GPU に必要な 3D 機能が無くても WARP を使えば CPU だけで動作します。
DirectX SDK November 2008 現在 10.1 相当で、Reference Driver よりずっと高速。
●手っ取り早く WARP を試す方法
install したサンプルの DXUT.cpp 2907行あたりに
pDeviceSettings->d3d11.DriverType= D3D_DRIVER_TYPE_WARP;
を挿入します。下記の赤い行。WARP Driver で起動するようになります。
// DXUT.cpp 2902行~ if( GetDXUTState().GetOverrideForceREF() ) pDeviceSettings- pDeviceSettings-
起動直後 = WARP
オプション画面から REFERENCE or HARDWARE に切り替え可能。(WARP には戻せない)
WARP device も FeatureLevel 10.1 なので、10.1 で動かないサンプルは動きません。
DirectX SDK November 2008 現在、動くのは MultithreadedRendering11 だけです。
実行速度は CPU 速度に依存します。
● Dynamic Shader Linkage
Direct3D 11 の大きな特徴の一つがこの Dynamic Shader Linkage です。
動的なリンクというと、複数のシェーダーバイナリをリンクし相互参照を解決する
イメージがありますが・・違います。
わかりやすく言えば「シェーダー内で関数ポインタが使えるようになった」ということ。
インスタンス自体は静的に生成されており、単一のシェーダーバイナリにすべての
インスタンス及びエントリポイントが含まれていなければなりません。
一つのシェーダープログラムの中に複数の関数を作成することが出来、その飛び先を
動的に切換えることができるわけです。
これらの仕組みは HLSL 上では class と interface の形で用いられます。
interface Base { float4 GetColor(); }; class Red : Base { float4 effect; float4 GetColor() { return float4( 1, 0, 0, 1 ); } }; class Green : Base { float4 effect; float4 GetColor() { return float4( 0, 1, 0, 1 ); } }; cbuffer ibuf { Red cbRed; Green cbGreen; }; Base color; // この宣言はポインタ相当で実体を持たない float4 main() : SV_Target { return color.GetColor(); }
上の例では Green と Red が interface Base を継承して作られています。
実行は Base で宣言された color を経由して行われており、どのメソッドが
呼び出されるのかコンパイル時にはわかりません。
Green 及び Red のインスタンスは cbRed, cbGreen として ConstantBuffer 上に
静的に作られています。外部の C++ 側からは、このインスタンス化された "cbRed"、
"cbGreen" を参照することが出来ます。
実際にどのインスタンスを color に割り当てるのか、レンダリング時に C++ 側で
いつでも切換えられるわけです。
シェーダー内のインスタンス参照には ID3D11ClassLinkage::GetClassInstance()
を使います。
ID3D11ClassLinkage* iClassLinkage; iDevice-
ID3D11DeviceContext::PSSetShader() で、シェーダーと同時に使用する
インスタンスのリストを渡します。
// Green を呼び出す場合 iContext-
Shader 内には複数の interface が宣言されている可能性があるので、必ずしも
上のように単純になりません。Reflection を参照して、PSSetShader() に渡す
インスタンスリストの何番目がどの interface 呼び出しに対応するのか調べる
必要があります。
class に変数が含まれている場合、ConstantBuffer 内のメモリ配置との対応付けも
必要です。(この場合 Effect ではないのでバッファの作成も必要)
Dynamic Shader Linkage の仕組みは、このようにパフォーマンスに影響が出ないよう
慎重に作られているため扱いは必ずしも簡単ではないようです。
今後 Direct3D11 対応の Effect fx_5_0 が完成したらある程度自動で行ってくれる
ようになるかもしれません。
出力されたコードを見ると fcall 命令が使われています。label の値 (おそらく
オフセット) を用いてサブルーチン呼び出しを行う仕組みだと考えられます。
ps_5_0 dcl_globalFlags refactoringAllowed dcl_function_body fb0 dcl_function_body fb1 dcl_function_table ft0 = {fb0} dcl_function_table ft1 = {fb1} dcl_interface fp0[1][1] = {ft0, ft1} dcl_output o0.xyzw dcl_temps 1 fcall fp0[0][0] mov o0.xy, r0.xyxx mov o0.zw, l(0,0,0,1.000000) ret label fb0 mov r0.xy, l(0,1.000000,0,0) ret label fb1 mov r0.xy, l(1.000000,0,0,0) ret
上記のように飛び先と種類はコンパイル時に決定しており、おそらく外部参照はできません。
このことは次の例を見てもわかります。
interface Base { float4 GetColor(); }; class Blue : Base { float4 GetColor() { return float4( 0, 0, 1, 1 ); } }; Base color; float4 main() : SV_Target { return color.GetColor(); }
この場合 interface を利用しつつも、継承されたエントリが Blue しかありません。
驚くことに HLSL コンパイラは Blue 以外が決して呼ばれないことを認識し、
間接呼び出しを取り除きます。
つまり main の中には Blue::GetColor() がインライン展開されてしまいます。
class の宣言自体は ShaderModel 4.1 (FeatureLevel 10.1) 以前でも使えます。
ただし間接呼び出しは出来ないので、interface からの呼び出しはエラーとなります。
上の例だと「Base color;」を「Blue color;」に書き換えればコンパイル可能です。
ちなみに従来の 4.1 以前のシェーダーにもサブルーチン呼び出しの機能自体は存在
していました。しかしながら HLSL コンパイラはすべてをインライン展開し尽くすので
実際に使われることはほとんどありません。
自分が知る限り、実際に関数呼び出しのコードが出力されるのは switch 文の
attribute に [call] を指定した場合だけでした。
Direct3D11 になってようやくシェーダー内のサブルーチン呼び出しが意味を持つ
ようになりました。
関連エントリ
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
ComputeShader の使い方はドキュメントも少なくまだよくわかっていません。
使用できる機能の共通性などから、PixelShader に近いか PixelShader の改良版
ではないかと予想していました。でも思ったより違いは多そうです。
Technical Preview (Beta) の段階なので、まだこれから仕様が変わる可能性もあります。
●作成と実行
シェーダーの生成はこれまでの PixelShader 等と同じです。
ShaderLinkage を指定できる点も同じ。
ステート設定は CS~ 系の命令を使います。
他のシェーダーとの違いは、任意のアドレスに書き込み可能なリソース UAV を
設定する専用命令 CSSetUnroderdAccessViews() があることです。
UAV (UnorderdAccessView) は、RWBuffer や RWTexture2D 等の書き込み可能な
リソースへのアクセスを意味しています。
ComputeShader はストリーム出力を持たないので Shader は void 宣言されます。
値を返すには必ず出力先として UAV の設定が必要となるようです。
UAV は PixelShader でも使えるはずなのに SetUnorderdAccessView 相当の命令は
ありません。この辺の仕様は要調査です。
将来 10 世代 GPU 対応の ComputeShader 4.0/4.1 が使えるようになった場合も
出力先の扱いがどうなるか気になるところです。
ComputeShader の実行は Draw() ではなく専用命令 Dispatch() を使います。
iContext-
引数はマニュアルに載ってませんが、走らせるスレッドの数と考えられます。
おそらく CUDA や AMD Stream SDK の thread や block、domain 等に相当する
パラメータでしょう。
●ウィンドウレス実行
ComputeShader は描画とは関係なく実行できるはずです。
そこで Window も DXGI も使わずにシェーダーを走らせてみました。
下記のシェーダーを実行すると結果は「360」。(45 x 2 x 4)
Windows や Present() とか無しにシェーダーが走りました。
Reference では InterlockedAdd でなく直接 a[0]+= tid.x と書いても同じ値に
なってしまいます。要注意です。
実際は Query 等を使って GPU の実行を待つ必要があるかもしれません。
●リソースの受け渡し
GPU が書き込めるリソースは D3D11_USAGE_DEFAULT だけなので、UAV は全部
D3D11_USAGE_DEFAULT になります。D3D11_USAGE_DEFAULT だと CPU で直接
読み出せないため、システム側に D3D11_USAGE_STAGING のミラーバッファを用意し
アクセスの度に CopyResource() しています。
初期化手順も多く、あまり簡単とはいえません。
CPU 側との連携やデータの読み書きの容易さを考えると、CUDA や AMD Stream SDK
を直接使った方が便利だと思います。おそらく CopyResource 相当の転送も自動で
やってくれています。
ComputeShader を使うメリットは、GPU 毎の SDK の違いを吸収できることと
Direct3D との連携の容易さでしょう。
Structured Buffer や Append Buffer も試そうとしましたが、フラグ設定や
組み合わせが複雑でなかなかうまくいきませんでした。
関連エントリ
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
使用できる機能の共通性などから、PixelShader に近いか PixelShader の改良版
ではないかと予想していました。でも思ったより違いは多そうです。
Technical Preview (Beta) の段階なので、まだこれから仕様が変わる可能性もあります。
●作成と実行
シェーダーの生成はこれまでの PixelShader 等と同じです。
ShaderLinkage を指定できる点も同じ。
ID3D11Device* iDevice; ID3D11ComputeShader* iCS; iDevice->CreateComputeShader( blob, blobsize, NULL, &iCS );
ステート設定は CS~ 系の命令を使います。
ID3D11DeviceContext* iContext; iContext->CSSetShaderResources( 0, 1, srvlist ); iContext->CSSetShader( iCS, NULL, 0 ); UINT uavcount= 256; iContext->CSSetUnroderdAccessViews( 0, 1, &iUAV0, &uavcount );
他のシェーダーとの違いは、任意のアドレスに書き込み可能なリソース UAV を
設定する専用命令 CSSetUnroderdAccessViews() があることです。
UAV (UnorderdAccessView) は、RWBuffer や RWTexture2D 等の書き込み可能な
リソースへのアクセスを意味しています。
ComputeShader はストリーム出力を持たないので Shader は void 宣言されます。
値を返すには必ず出力先として UAV の設定が必要となるようです。
UAV は PixelShader でも使えるはずなのに SetUnorderdAccessView 相当の命令は
ありません。この辺の仕様は要調査です。
将来 10 世代 GPU 対応の ComputeShader 4.0/4.1 が使えるようになった場合も
出力先の扱いがどうなるか気になるところです。
ComputeShader の実行は Draw() ではなく専用命令 Dispatch() を使います。
iContext-
引数はマニュアルに載ってませんが、走らせるスレッドの数と考えられます。
おそらく CUDA や AMD Stream SDK の thread や block、domain 等に相当する
パラメータでしょう。
●ウィンドウレス実行
ComputeShader は描画とは関係なく実行できるはずです。
そこで Window も DXGI も使わずにシェーダーを走らせてみました。
D3D11CreateDevice( NULL, //D3D_DRIVER_TYPE_HARDWARE, //D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE, NULL, D3D11_CREATE_DEVICE_DEBUG, NULL, 0, D3D11_SDK_VERSION, &iDevice, NULL, &iContext ); // hlsl 読み込みは省略 → memory ID3DBlob* codeblob; D3DCompile( memory, (size_t)size, fname, NULL, // macro NULL, // include "main", "cs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &codeblob, &errblob ); iDevice->CreateComputeShader( codeblob->GetBufferPointer(), codeblob->GetBufferSize(), NULL, &iCS ); codeblob->Release(); // 出力先 ID3D11Buffer* iBufferC; D3D11_BUFFER_DESC bdesc; bdesc.Usage= D3D11_USAGE_DEFAULT; bdesc.BindFlags= D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS; bdesc.CPUAccessFlags= 0; bdesc.MiscFlags= 0; bdesc.ByteWidth= sizeof(float)*4*256; bdesc.StructureByteStride= sizeof(float); iDevice->CreateBuffer( &bdesc, NULL, &iBufferC ); ID3D11UnorderedAccessView* iUAView; D3D11_UNORDERED_ACCESS_VIEW_DESC uavdesc; uavdesc.Format= DXGI_FORMAT_R32_UINT; uavdesc.ViewDimension= D3D11_UAV_DIMENSION_BUFFER; uavdesc.Buffer.FirstElement= 0; uavdesc.Buffer.NumElements= 256; uavdesc.Buffer.Flags= 0; iDevice->CreateUnorderedAccessView( iBufferC, &uavdesc, &iUAView ); // CPUアクセス用バッファ ID3D11Buffer* iBufferC2; D3D11_BUFFER_DESC bdesc; bdesc.Usage= D3D11_USAGE_STAGING; bdesc.BindFlags= 0; bdesc.CPUAccessFlags= D3D11_CPU_ACCESS_WRITE|D3D11_CPU_ACCESS_READ; bdesc.MiscFlags= 0; bdesc.ByteWidth= sizeof(float)*4*256; bdesc.StructureByteStride= sizeof(float); iDevice->CreateBuffer( &bdesc, NULL, &iBufferC2 ); // 初期データ書き込み D3D11_MAPPED_SUBRESOURCE mapres; iContext->Map( iBufferC2, 0, D3D11_MAP_WRITE, 0, &mapres ); unsigned int* ptr= reinterpret_cast<unsigned int*>( mapres.pData ); ptr[0]= 0; iContext->Unmap( iBufferC2, 0 ); iContext->CopyResource( iBufferC, iBufferC2 ); // 実行 for( int i= 0 ; i< 4 ; i++ ){ iContext->CSSetShader( iCS, NULL, 0 ); UINT uavcount= 256; iContext->CSSetUnorderedAccessViews( 0, 1, &iUAView, &uavcount ); iContext->Dispatch( 10, 2, 1 ); } // 結果読み込みと表示 iContext->CopyResource( iBufferC2, iBufferC ); D3D11_MAPPED_SUBRESOURCE mapres; iContext->Map( iBufferC2, 0, D3D11_MAP_READ, 0, &mapres ); unsigned int* ptr= reinterpret_cast<unsigned int*>( mapres.pData ); printf( "%d\n", ptr[0] ); iContext->Unmap( iBufferC2, 0 );
下記のシェーダーを実行すると結果は「360」。(45 x 2 x 4)
Windows や Present() とか無しにシェーダーが走りました。
// cs.hlsl RWBuffer<uint> a; [numthreads(1,1,1)] void main( uint3 tid : SV_DispatchThreadID ) { InterlockedAdd( a[0], tid.x ); }
Reference では InterlockedAdd でなく直接 a[0]+= tid.x と書いても同じ値に
なってしまいます。要注意です。
実際は Query 等を使って GPU の実行を待つ必要があるかもしれません。
●リソースの受け渡し
GPU が書き込めるリソースは D3D11_USAGE_DEFAULT だけなので、UAV は全部
D3D11_USAGE_DEFAULT になります。D3D11_USAGE_DEFAULT だと CPU で直接
読み出せないため、システム側に D3D11_USAGE_STAGING のミラーバッファを用意し
アクセスの度に CopyResource() しています。
初期化手順も多く、あまり簡単とはいえません。
CPU 側との連携やデータの読み書きの容易さを考えると、CUDA や AMD Stream SDK
を直接使った方が便利だと思います。おそらく CopyResource 相当の転送も自動で
やってくれています。
ComputeShader を使うメリットは、GPU 毎の SDK の違いを吸収できることと
Direct3D との連携の容易さでしょう。
Structured Buffer や Append Buffer も試そうとしましたが、フラグ設定や
組み合わせが複雑でなかなかうまくいきませんでした。
関連エントリ
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2012/11/11
Windows Phone 8 SDK と Direct3D 11.1
Windows Phone 8 では描画 API として Direct3D が使えるようになっています。
iOS/Android から遅れること 3 年、WindowsPhone でもようやく Shader が
使えるようになりそうです。
・Microsoft Windows Phone SDK 8.0
WindowsCE だった WindowsMobile/WindowsPhone 7 までは、
Desktop の Intel/AMD、Mobile の ARM と OS だけでなく
アーキテクチャでも明確な切り分けが行われていました。
Windows 8 RT が Desktop / Tablet で ARM をサポートするようになり、
今後ハードウエアも OS/API も統合されていくものと考えられます。
Windows Phone 8 SDK では開発言語として Native C/C++ が使えるようになり、
描画 API も Direct3D がサポートされています。
iOS/Android 含めたプラットフォーム非依存の共通言語として期待できます。
もっとも WindowsCE の時代、WindowsMobile 6 までは Win32 API と C/C++ で
開発していたので WindowsPhone 7 だけが特別だったのかもしれません。
● Windows Phone 8 SDK
Windows Phone 8 SDK は開発環境が統合されており、
iOS の Xcode のようにインストールするだけで一式揃います。
WindowsCE 時代の eMbedded Visual Tools を思い出します。
注意点は、Windows Phone 8 SDK のインストールに Windows 8 x64 が
必須となっていることです。
その理由の一つに Windows Phone 8 Emulator の Hyper-V があるようです。
インストール時にオプション選択が無いので、対応 CPU では常に Hyper-V が
有効となり、未対応 CPU では Emulator 無しでインストールが行われます。
他の仮想 PC 系ソフトを併用している場合は競合することがあるので注意が必要です。
● Direct3D 11 10Level9
GPU 用 3D API は Desktop と同じ Direct3D11 です。
現時点で Windows Phone 8 が対応する feature level は 9_3 となっています。
よって GPU 機能は OpenGL ES 2.0 と同等で、
Direct3D 11 といっても Geometry/Hull/Domain/ComputeShader 等が
使えるわけではありません。
・Direct3D feature levels (Windows)
WindowsVista 登場時に Direct3D10 がリリースされましたが、
DirectX 9 以前とは方針が異なり、一切下位互換を持たない仕様でした。
Direct3D 9 以下 (ShaderModel 4.0未満) の GPU は切り捨てられ、
新しい Direct3D 10 API は Direct3D 10 対応 GPU (ShaderModel 4.0以上)
でなければ使えませんでした。
そのかわり caps といった細かなハードウエア機能のチェックが不要です。
その後 Windows 7 で更新された Direct3D 11 は再び路線を変更し、
DirectX 9 以前と同じように API セットのレベルと GPU のハードウエア機能の
レベルが分離されました。下位互換が復活しています。
・Direct3D 11 on DOwnlevel Hardware (Windows)
Direct3D 9/Direct3D 10 相当 (ShaderModel 5.0未満) の GPU でも
ドライバさえ対応していれば新しい API 経由で使えるわけです。
その代償として徐々に caps (feature level) が復活しつつあります。
Feature Level は blog の過去記事だとこのあたり↓で触れています。
・2008/11/09 Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・2008/11/06 Direct3D11 Technical Preview D3D11の互換性、WARP Driver
● Windows 8 と Direct3D 11.1
メジャーアップデートだった Vista や 7 とは異なり、
Windows 8 の Direct3D は 11.0 から 11.1 と小規模な拡張にとどまっています。
特に Shader Model が 5.0 のままで、5.1 に更新されていないのは意外でした。
11.1 では GPU のハードウエア進化に合わせたハイエンド向けの機能拡張だけでなく、
むしろ Mobile GPU 向けの機能が大幅に追加されているようです。
時代を反映していると言えます。
例えば D3D11_FEATURE (Caps) の項目が 11.1 で著しく増えています。
Windows 8 RT や WindowsPhone 8 など OpenGL ES 2.0 GPU を
10Level9 でサポートするために必要になったと思われる項目が多数あります。
D3D11_FEATURE_DATA_ARCHITECTURE_INFO の
TileBasedDeferredRenderer はまさに Mobile GPU 向けの Caps で、
D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT
SupportsDepthAsTextureWithLessEqualComparisonFilter も
ShaderModel 4.0 以降は標準のものです。
D3D11_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT も
OpenGL ES 2.0 なら非常に馴染みが深い代物でしょう。
● Mobile GPU と演算精度
OpenGL ES 2.0 用 GPU で最適化を行う場合、シェーダーの演算精度は
非常に大きな問題となります。
shader 内で highp/mediump/lowp の適切な使い分けが要求されます。
・Mobile GPU の比較 : Precision
・OpenGL ES 2.0 GLSL precision 宣言と GPU 毎の演算精度を調べる
DirectX の場合、DirectX 9 初期 GeForce FX 5800 系の half 型以外では
演算精度を気にする必要がほとんどありませんでした。
11.1 の HLSL では GLSL の highp / mediump / lowp 同様、
最低限必要な演算精度を宣言ができるようになっています。
OpenGL ES 2.0 同様に、WindowsPhone 8 や Windows RT で
最適化する場合に必要になってくるものと思われます。
● ShaderModel 5.0 アセンブラ命令
いつの間にかマニュアルに載っていました。
アセンブラでシェーダーを記述するわけではありませんがデバッグ時に役に立ちます。
・Shader Model 5 Assembly (Windows)
Direct3D 10 (ShaderModel 4.0) 時代は載っていなかったので自分で調べていました。
・ShaderModel4.0 アセンブラ命令
● DirectX SDK
Windows Phone 8 SDK と同じように、
Desktop 向け Direct3D 11.1 SDK は Windows SDK に統合されています。
・Windows Software Development Kit (SDK) for Windows 8
VisualStudio Express 2012 for Windows Desktop の Windows SDK で
dll 番号を見ると 46 になっていました。
・DirectX SDK Version 一覧
関連エントリ
・2012/02/24 OpenGL ES 2.0 GLSL precision 宣言と GPU 毎の演算精度を調べる
・2008/11/09 Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・2008/11/06 Direct3D11 Technical Preview D3D11の互換性、WARP Driver
iOS/Android から遅れること 3 年、WindowsPhone でもようやく Shader が
使えるようになりそうです。
・Microsoft Windows Phone SDK 8.0
WindowsCE だった WindowsMobile/WindowsPhone 7 までは、
Desktop の Intel/AMD、Mobile の ARM と OS だけでなく
アーキテクチャでも明確な切り分けが行われていました。
Windows 8 RT が Desktop / Tablet で ARM をサポートするようになり、
今後ハードウエアも OS/API も統合されていくものと考えられます。
Windows Phone 8 SDK では開発言語として Native C/C++ が使えるようになり、
描画 API も Direct3D がサポートされています。
iOS/Android 含めたプラットフォーム非依存の共通言語として期待できます。
もっとも WindowsCE の時代、WindowsMobile 6 までは Win32 API と C/C++ で
開発していたので WindowsPhone 7 だけが特別だったのかもしれません。
● Windows Phone 8 SDK
Windows Phone 8 SDK は開発環境が統合されており、
iOS の Xcode のようにインストールするだけで一式揃います。
WindowsCE 時代の eMbedded Visual Tools を思い出します。
注意点は、Windows Phone 8 SDK のインストールに Windows 8 x64 が
必須となっていることです。
その理由の一つに Windows Phone 8 Emulator の Hyper-V があるようです。
インストール時にオプション選択が無いので、対応 CPU では常に Hyper-V が
有効となり、未対応 CPU では Emulator 無しでインストールが行われます。
他の仮想 PC 系ソフトを併用している場合は競合することがあるので注意が必要です。
● Direct3D 11 10Level9
GPU 用 3D API は Desktop と同じ Direct3D11 です。
現時点で Windows Phone 8 が対応する feature level は 9_3 となっています。
よって GPU 機能は OpenGL ES 2.0 と同等で、
Direct3D 11 といっても Geometry/Hull/Domain/ComputeShader 等が
使えるわけではありません。
・Direct3D feature levels (Windows)
WindowsVista 登場時に Direct3D10 がリリースされましたが、
DirectX 9 以前とは方針が異なり、一切下位互換を持たない仕様でした。
Direct3D 9 以下 (ShaderModel 4.0未満) の GPU は切り捨てられ、
新しい Direct3D 10 API は Direct3D 10 対応 GPU (ShaderModel 4.0以上)
でなければ使えませんでした。
そのかわり caps といった細かなハードウエア機能のチェックが不要です。
その後 Windows 7 で更新された Direct3D 11 は再び路線を変更し、
DirectX 9 以前と同じように API セットのレベルと GPU のハードウエア機能の
レベルが分離されました。下位互換が復活しています。
・Direct3D 11 on DOwnlevel Hardware (Windows)
Direct3D 9/Direct3D 10 相当 (ShaderModel 5.0未満) の GPU でも
ドライバさえ対応していれば新しい API 経由で使えるわけです。
その代償として徐々に caps (feature level) が復活しつつあります。
Feature Level は blog の過去記事だとこのあたり↓で触れています。
・2008/11/09 Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・2008/11/06 Direct3D11 Technical Preview D3D11の互換性、WARP Driver
● Windows 8 と Direct3D 11.1
メジャーアップデートだった Vista や 7 とは異なり、
Windows 8 の Direct3D は 11.0 から 11.1 と小規模な拡張にとどまっています。
特に Shader Model が 5.0 のままで、5.1 に更新されていないのは意外でした。
11.1 では GPU のハードウエア進化に合わせたハイエンド向けの機能拡張だけでなく、
むしろ Mobile GPU 向けの機能が大幅に追加されているようです。
時代を反映していると言えます。
例えば D3D11_FEATURE (Caps) の項目が 11.1 で著しく増えています。
Windows 8 RT や WindowsPhone 8 など OpenGL ES 2.0 GPU を
10Level9 でサポートするために必要になったと思われる項目が多数あります。
D3D11_FEATURE_DATA_ARCHITECTURE_INFO の
TileBasedDeferredRenderer はまさに Mobile GPU 向けの Caps で、
D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT
SupportsDepthAsTextureWithLessEqualComparisonFilter も
ShaderModel 4.0 以降は標準のものです。
D3D11_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT も
OpenGL ES 2.0 なら非常に馴染みが深い代物でしょう。
● Mobile GPU と演算精度
OpenGL ES 2.0 用 GPU で最適化を行う場合、シェーダーの演算精度は
非常に大きな問題となります。
shader 内で highp/mediump/lowp の適切な使い分けが要求されます。
・Mobile GPU の比較 : Precision
・OpenGL ES 2.0 GLSL precision 宣言と GPU 毎の演算精度を調べる
DirectX の場合、DirectX 9 初期 GeForce FX 5800 系の half 型以外では
演算精度を気にする必要がほとんどありませんでした。
11.1 の HLSL では GLSL の highp / mediump / lowp 同様、
最低限必要な演算精度を宣言ができるようになっています。
OpenGL GLSL Direct3D HLSL -------------------------------------- highp float float mediump float min16float (half) lowp float min10float
OpenGL ES 2.0 同様に、WindowsPhone 8 や Windows RT で
最適化する場合に必要になってくるものと思われます。
● ShaderModel 5.0 アセンブラ命令
いつの間にかマニュアルに載っていました。
アセンブラでシェーダーを記述するわけではありませんがデバッグ時に役に立ちます。
・Shader Model 5 Assembly (Windows)
Direct3D 10 (ShaderModel 4.0) 時代は載っていなかったので自分で調べていました。
・ShaderModel4.0 アセンブラ命令
● DirectX SDK
Windows Phone 8 SDK と同じように、
Desktop 向け Direct3D 11.1 SDK は Windows SDK に統合されています。
・Windows Software Development Kit (SDK) for Windows 8
VisualStudio Express 2012 for Windows Desktop の Windows SDK で
dll 番号を見ると 46 になっていました。
・DirectX SDK Version 一覧
関連エントリ
・2012/02/24 OpenGL ES 2.0 GLSL precision 宣言と GPU 毎の演算精度を調べる
・2008/11/09 Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・2008/11/06 Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2008/11/08
Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
引き続き DirectX SDK Novemver 2008 より、Direct3D 11 の Technical Preview です。
今まで取り上げたことがないもの中心。
●リソースサイズ
仕様として扱えるリソースの上限が拡張されています。
4GByte (32bit) を超えるリソースも扱えるらしく、内部的には 64bit で管理されて
いるようです。すでに VRAM 2GB の製品は存在しているので、4GB を超えるのも
時間の問題と思われます。
リソースサイズが 32bit を超えていても、データアクセス時の index は 32bit までに
制限されます。shader に double は追加されたものの 64bit int 型はまだ無いので
当然かもしれません。
●64bit 整数
HLSL では long/ulong 型が 64bit 整数となるようです。
double 対応前の HLSL コンパイラでは double 型を使うと float 相当の扱いでした。
(D3D10/DX10 シェーダーと64bit浮動少数)
新しい fxc で long/ulong を使うと未サポートエラーになります。
: error X3653: 'v': ps_5_0 does not support 64-bit integers
: error X3653: 'v': cs_5_0 does not support 64-bit integers
●fxc プロファイル
付属の fxc.exe は単一で、d3d9/d3d10/d3d11 共通になっています。
fxc の対応プロファイルを見ると、ComputeShader は ShaderModel 4.0/4.1 でも
使えそうです。
前前回 FeatureLevel 9.1 と 9.2 の違いがわからないと書いたけど、これを見ると
本当にわかりません。
● RW Buffer
読み書き可能リソースが追加されています。
ついに、シェーダーが直接リソースに書き込みできるようになりました。
ストリーム以外の出力ができます。
RWBuffer
RWByteAddressBuffer
RWStructuredBuffer
RWTexture1D
RWTexture1DArray
RWTexture2D
RWTexture2DArray
RWTexture3D
書き込みできるのは PixelShader と ComputeShader のみ。
Load() メソッドではなく基本的に operator[] を使用します。
例えば Buffer では Load() と両方使えるものの RWBuffer は必ず operator[] を
使う必要があります。
Atomic 命令が使えるのは RWByteAddressBuffer だけで、こちらは逆に operator[] が
使えません。Load()/Store() や Interlocked 命令を使用します。
下記のようにコンパイルされました。(fxc /Tps_5_0)
store_ 、atomic_ 命令が存在しておりやっと実感がわきます。
シェーダーが本当に書き込んでいます。
dcl_uav や ld_uav など、uav と付くのが読み書き可能な RW リソースを指している
ようです。t レジスタではなく u レジスタが使われています。
ちなみに 4.0 をターゲットにすると UAV をサポートしていないとのエラーが出ます。
UAV は Unordered Access View (ID3D11UnorderedAccessView) の略だそうです。
● Append/Consume Buffer
RW Buffer や Atomic 命令だけでなく、もう少し便利な同期機能も追加されています。
AppendStructuredBuffer
AppendByteAddressBuffer
ConsumeStructuredBuffer
ConsumeByteAddressBuffer
Append がバッファの最後に追加、Consume がバッファの最後から取り出します。
LIFO として機能するようです。
RW Buffer と違い、読み込み専用、書き込み専用とそれぞれ個別に定義します。
送信側と受け側を明確にし、同期を単純化しているものと思われます。
使用可能なのはやはり PixelShader と ComputeShader のみ。
マニュアルでは一部全シェーダーで使えるかのような表記もありますがおそらく間違いでしょう。
使い方がよくわからなかったのでコンパイルのみ試したところ、PixelShader だとたまに
Internal Compiler Error が出るようです。
やはり主な用途は ComputeShader だと思われます。
コンパイルした結果は下記の通りです。(fxc /Tcs_5_0)
imm_atomic_consume / imm_atomic_alloc は、このペアを使って load や store を
行うと、atomic な操作でかつバッファサイズも増減することを意味しているようです。
例えば uint を float4 に変更すると次の通り。
Append / Consume 操作は必ずスカラー単位で行われているようです。
これだけだと、float4 の間に他の Append/Consume が割り込んでしまいそうな
気がしますが、offset も同時に指定しているし、まだよくわかりません。
バッファとしては UAV の一種で、Dim が consume 及び append となっています。
関連エントリ
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
・Direct3D11 シェーダーの動的リンクやテクスチャ
・Direct3D11 マルチスレッドのための機能
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
今まで取り上げたことがないもの中心。
●リソースサイズ
仕様として扱えるリソースの上限が拡張されています。
4GByte (32bit) を超えるリソースも扱えるらしく、内部的には 64bit で管理されて
いるようです。すでに VRAM 2GB の製品は存在しているので、4GB を超えるのも
時間の問題と思われます。
リソースサイズが 32bit を超えていても、データアクセス時の index は 32bit までに
制限されます。shader に double は追加されたものの 64bit int 型はまだ無いので
当然かもしれません。
●64bit 整数
HLSL では long/ulong 型が 64bit 整数となるようです。
double 対応前の HLSL コンパイラでは double 型を使うと float 相当の扱いでした。
(D3D10/DX10 シェーダーと64bit浮動少数)
新しい fxc で long/ulong を使うと未サポートエラーになります。
: error X3653: 'v': ps_5_0 does not support 64-bit integers
: error X3653: 'v': cs_5_0 does not support 64-bit integers
●fxc プロファイル
付属の fxc.exe は単一で、d3d9/d3d10/d3d11 共通になっています。
fxc の対応プロファイルを見ると、ComputeShader は ShaderModel 4.0/4.1 でも
使えそうです。
fx_2_0 fx_4_0 fx_4_1 cs_4_0 // ← ComputeShader 4.0? cs_4_1 // ← ComputeShader 4.1? cs_5_0 gs_4_0 gs_4_1 gs_5_0 ds_5_0 // DomainShader hs_5_0 // HullShader ps_2_0 ps_2_a // NVIDIA拡張 ps_2_b // ATI拡張 ps_2_sw ps_3_0 ps_3_sw ps_4_0 ps_4_0_level_9_1 // D3D11 の ShaderModel2.0 ps_4_0_level_9_3 // D3D11 の ShaderModel3.0 ps_4_1 ps_5_0 vs_1_1 vs_2_0 vs_2_a // NVIDIA拡張 vs_2_sw vs_3_0 vs_3_sw vs_4_0 vs_4_0_level_9_1 // D3D11 の ShaderModel2.0 vs_4_0_level_9_3 // D3D11 の ShaderModel3.0 vs_4_1 vs_5_0 tx_1_0
前前回 FeatureLevel 9.1 と 9.2 の違いがわからないと書いたけど、これを見ると
本当にわかりません。
● RW Buffer
読み書き可能リソースが追加されています。
ついに、シェーダーが直接リソースに書き込みできるようになりました。
ストリーム以外の出力ができます。
RWBuffer
RWByteAddressBuffer
RWStructuredBuffer
RWTexture1D
RWTexture1DArray
RWTexture2D
RWTexture2DArray
RWTexture3D
書き込みできるのは PixelShader と ComputeShader のみ。
Load() メソッドではなく基本的に operator[] を使用します。
例えば Buffer では Load() と両方使えるものの RWBuffer は必ず operator[] を
使う必要があります。
Atomic 命令が使えるのは RWByteAddressBuffer だけで、こちらは逆に operator[] が
使えません。Load()/Store() や Interlocked 命令を使用します。
RWBuffera; RWByteAddressBuffer b; Buffer c; float4 main() : SV_Target { a[1]= 3; b.Store( 0, 4 ); b.InterlockedAdd( 0, 1 ); return a[0]+ c.Load( 0 ); }
下記のようにコンパイルされました。(fxc /Tps_5_0)
ps_5_0 dcl_globalFlags refactoringAllowed dcl_resource_buffer (float,float,float,float) t0 dcl_uav_typed_buffer (float,float,float,float) u1 dcl_uav_raw u2 dcl_output o0.xyzw dcl_temps 2 store_uav_typed u1.xyzw, l(1, 1, 1, 1), l(0x40400000, 0x40400000, 0x40400000, 0x40400000) store_raw u2.x, l(0), l(4) atomic_iadd u2, l(0), l(1) ld_uav_typed r0.xyzw, l(0, 0, 0, 0), u1.xyzw ld_indexable(buffer)(float,float,float,float) r1.xyzw, l(0, 0, 0, 0), t0.xyzw add o0.xyzw, r0.xyzw, r1.xyzw ret
store_ 、atomic_ 命令が存在しておりやっと実感がわきます。
シェーダーが本当に書き込んでいます。
dcl_uav や ld_uav など、uav と付くのが読み書き可能な RW リソースを指している
ようです。t レジスタではなく u レジスタが使われています。
ちなみに 4.0 をターゲットにすると UAV をサポートしていないとのエラーが出ます。
// Resource Bindings: // // Name Type Format Dim Slot Elements // ---------------- ---------- ------- ----------- ---- -------- // c texture float4 buf 0 1 // a UAV float4 buf 1 1 // b UAV byte rw 2 1
UAV は Unordered Access View (ID3D11UnorderedAccessView) の略だそうです。
● Append/Consume Buffer
RW Buffer や Atomic 命令だけでなく、もう少し便利な同期機能も追加されています。
AppendStructuredBuffer
AppendByteAddressBuffer
ConsumeStructuredBuffer
ConsumeByteAddressBuffer
Append がバッファの最後に追加、Consume がバッファの最後から取り出します。
LIFO として機能するようです。
RW Buffer と違い、読み込み専用、書き込み専用とそれぞれ個別に定義します。
送信側と受け側を明確にし、同期を単純化しているものと思われます。
使用可能なのはやはり PixelShader と ComputeShader のみ。
マニュアルでは一部全シェーダーで使えるかのような表記もありますがおそらく間違いでしょう。
使い方がよくわからなかったのでコンパイルのみ試したところ、PixelShader だとたまに
Internal Compiler Error が出るようです。
やはり主な用途は ComputeShader だと思われます。
ConsumeStructuredBufferc; AppendStructuredBuffer a; [numthreads(1,1,1)] void main() { uint t= c.Consume(); a.Append( t ); }
コンパイルした結果は下記の通りです。(fxc /Tcs_5_0)
cs_5_0 dcl_globalFlags refactoringAllowed dcl_uav_structured u0, 4 dcl_uav_structured u1, 4 dcl_temps 1 dcl_thread_group 1, 1, 1 imm_atomic_consume r0.x, u0 imm_atomic_alloc r0.y, u1 ld_structured r0.x, r0.x, l(0), u0.x store_structured u1.x, r0.y, l(0), r0.x ret
imm_atomic_consume / imm_atomic_alloc は、このペアを使って load や store を
行うと、atomic な操作でかつバッファサイズも増減することを意味しているようです。
例えば uint を float4 に変更すると次の通り。
ConsumeStructuredBufferc; AppendStructuredBuffer a; [numthreads(1,1,1)] void main() { float4 t= c.Consume(); a.Append( t ); } cs_5_0 dcl_globalFlags refactoringAllowed dcl_uav_structured u0, 16 dcl_uav_structured u1, 16 dcl_temps 2 dcl_thread_group 1, 1, 1 imm_atomic_consume r0.x, u0 ld_structured r0.y, r0.x, l(0), u0.x ld_structured r0.z, r0.x, l(4), u0.x ld_structured r0.w, r0.x, l(8), u0.x ld_structured r0.x, r0.x, l(12), u0.x imm_atomic_alloc r1.x, u1 store_structured u1.x, r1.x, l(0), r0.y store_structured u1.x, r1.x, l(4), r0.z store_structured u1.x, r1.x, l(8), r0.w store_structured u1.x, r1.x, l(12), r0.x ret
Append / Consume 操作は必ずスカラー単位で行われているようです。
これだけだと、float4 の間に他の Append/Consume が割り込んでしまいそうな
気がしますが、offset も同時に指定しているし、まだよくわかりません。
// Name Type Format Dim Slot Elements // ---------------- ---------- ------- ----------- ---- -------- // c UAV struct consume 0 1 // a UAV struct append 1 1
バッファとしては UAV の一種で、Dim が consume 及び append となっています。
関連エントリ
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
・Direct3D11 シェーダーの動的リンクやテクスチャ
・Direct3D11 マルチスレッドのための機能
・Direct3D11 テセレータとシェーダー
・Direct3D11 のパイプライン
・Direct3D11 Compute Shader など
・Gamefest2008 と Direct3D 11
2009/02/04
Direct2D (3) 互換性の検証と Vista で Direct2D
こちらで書いたように、Windows7 では Direct3D11 と同じく
Direct3D10.1 でも下位の GPU が新たにサポートされることになりました。
実際にデバイスを作成して試してみました。
今度は Direct2D ではなく、直接 Direct3D10.1 ( D3D10CreateDevice1() ) を
使用しています。
すべて成功しました。
これは、今までの Windows Vista + Direct3D10.0 / Direct3D10.1 では
出来なかったことなのです。
まず Windows SDK for Windows 7 BETA を install しなければ、
D3D10_FEATURE_LEVEL_9_1 ~ D3D10_FEATURE_LEVEL_9_3 のシンボルが
定義されていないということ。
Vista 以前で使える現在の DirectX SDK ではコンパイル出来ませんでした。
DirectX SDK Nov2008 の help では、ヘッダファイルにシンボルが無いのに次の
ように書かれていたりします。
この Windows7 用の FEATURE テストプログラムを WindowsVista 上で走らせて
みたところ、全く同じようにデバイスの生成が成功してしまいました。
考えられるのは Windows Vista でも Direct3D SDK の Direct3D11 beta ランタイム
を導入した時点で使えるようになっていたのではないかということ。
Direct3D11 では実際に D3D9_1~3 の下位の FeatureLevel に対応しています。
Direct3D10.1 で同じように利用できてもおかしくないのかもしれません。
過去の DirectX はすべて互換性を保ちながら移行するのが当たり前でした。
実際に Direct3D 9 は Direct3D 8 のハードウエアでも動作します。
デバイスは作れますし API も呼べますしシェーダーモデル 1 を扱うことが出来ます。
Direct3D9 の特徴であるシェーダーモデル 2~3 は利用できませんが、それは
Direct3D10.1 で Level9 を扱っても全く同じこと。
さらに前、たとえば DirectX7 までは毎年メジャーナンバーがアップデートされる
ことになっていました。わずか 1年で GPU が使えなくなっていたら、とても
ついて行くのが大変ですよね。
・DirectX/GPU 年表
・DirectX list
互換性を完全に切り捨てたのは Direct3D9 → Direct3D10 のタイミングだけなのです。
ドライバモデルが変わったり OS と融合されたりと、内部的に困難な理由が
いろいろあったのではないかと考えられます。
だから今回 Windows7 と同じタイミングで、Windows7 だけでなく Vista でも
同等の機能がサポートされるのは画期的なことかもしれません。
でも古くから DirectX を触ってるものからすれば画期的でも何でもなくて、
互換性の切り捨てを撤回して、やっぱり以前と同じように使えるようにしました、
といってるだけに見えるのです。
● Windows7 SDK の導入
Windows7 SDK があれば、Vista でも Direct3D10.1 で Direct3D9 GPU 向けの
プログラムを作成できることがわかりました。
DirectX SDK の更新と思って Windows SDK for Windows 7 BETA を早めに導入して
おくのもありかもしれません。
あらかじめ DirectX SDK November 2008 をインストールしておく必要があります。
VisualStudio2008 を起動する前に、スタートメニューから
Visual Studio Registration → Windows SDK Configuration Tool
を起動して SDK を切り替えておきます。
include パスの順番には注意が必要です。
Tools → Options → Projects and Solutions → VC++ Directories
Win32 / Include files
$(DXSDK_DIR)\include よりも上に $(WindowsSdkDir)\include が来るようにします。
DirectX SDK の install で直接 C:\Program Files ~とパスが書き込まれている
ことがあるので、その行は削除して $(DXSDK_DIR)include に置き換えておくことを
お勧めします。
lib も同じようにします。
● Vista で Direct2D が動く
Windows7 SDK beta があれば Direct2D のプログラムを作ることが出来ます。
実際に走らせるには Windows7 beta をインストールした PC が必要です。
Direct2D は結局 Direct3D10.1 を呼び出しているだけのはず。
Direct3D10.1 で下位 GPU もなぜか動くことがわかったので、もしやと思って
Windows7 beta から d2d1.dll だけ vista に持ってきたら拍子抜けするほどあっさり
動作しました。
とりあえず build したプログラムと同じ場所に d2d1.dll と DWrite.dll だけ置いておけば
Vsita でも Direct2D のプログラムを作って走らせることが出来るようです。
関連エントリ
・Direct2D その(2) インターフェース
・Direct2D と Direct3D10.1 の下位互換
Direct3D10.1 でも下位の GPU が新たにサポートされることになりました。
実際にデバイスを作成して試してみました。
今度は Direct2D ではなく、直接 Direct3D10.1 ( D3D10CreateDevice1() ) を
使用しています。
Direct3D10.1 D3D10_FEATURE_LEVEL_10_0 D3D10_FEATURE_LEVEL_10_1 D3D10_FEATURE_LEVEL_9_1 D3D10_FEATURE_LEVEL_9_2 D3D10_FEATURE_LEVEL_9_3
すべて成功しました。
これは、今までの Windows Vista + Direct3D10.0 / Direct3D10.1 では
出来なかったことなのです。
まず Windows SDK for Windows 7 BETA を install しなければ、
D3D10_FEATURE_LEVEL_9_1 ~ D3D10_FEATURE_LEVEL_9_3 のシンボルが
定義されていないということ。
Vista 以前で使える現在の DirectX SDK ではコンパイル出来ませんでした。
DirectX SDK Nov2008 の help では、ヘッダファイルにシンボルが無いのに次の
ように書かれていたりします。
D3D10_FEATURE_LEVEL_9_1 This value is in the header, but this feature is not yet implemented. D3D10_FEATURE_LEVEL_9_2 This value is in the header, but this feature is not yet implemented. D3D10_FEATURE_LEVEL_9_3 This value is in the header, but this feature is not yet implemented.
この Windows7 用の FEATURE テストプログラムを WindowsVista 上で走らせて
みたところ、全く同じようにデバイスの生成が成功してしまいました。
考えられるのは Windows Vista でも Direct3D SDK の Direct3D11 beta ランタイム
を導入した時点で使えるようになっていたのではないかということ。
Direct3D11 では実際に D3D9_1~3 の下位の FeatureLevel に対応しています。
Direct3D10.1 で同じように利用できてもおかしくないのかもしれません。
過去の DirectX はすべて互換性を保ちながら移行するのが当たり前でした。
実際に Direct3D 9 は Direct3D 8 のハードウエアでも動作します。
デバイスは作れますし API も呼べますしシェーダーモデル 1 を扱うことが出来ます。
Direct3D9 の特徴であるシェーダーモデル 2~3 は利用できませんが、それは
Direct3D10.1 で Level9 を扱っても全く同じこと。
さらに前、たとえば DirectX7 までは毎年メジャーナンバーがアップデートされる
ことになっていました。わずか 1年で GPU が使えなくなっていたら、とても
ついて行くのが大変ですよね。
・DirectX/GPU 年表
・DirectX list
互換性を完全に切り捨てたのは Direct3D9 → Direct3D10 のタイミングだけなのです。
ドライバモデルが変わったり OS と融合されたりと、内部的に困難な理由が
いろいろあったのではないかと考えられます。
だから今回 Windows7 と同じタイミングで、Windows7 だけでなく Vista でも
同等の機能がサポートされるのは画期的なことかもしれません。
でも古くから DirectX を触ってるものからすれば画期的でも何でもなくて、
互換性の切り捨てを撤回して、やっぱり以前と同じように使えるようにしました、
といってるだけに見えるのです。
● Windows7 SDK の導入
Windows7 SDK があれば、Vista でも Direct3D10.1 で Direct3D9 GPU 向けの
プログラムを作成できることがわかりました。
DirectX SDK の更新と思って Windows SDK for Windows 7 BETA を早めに導入して
おくのもありかもしれません。
あらかじめ DirectX SDK November 2008 をインストールしておく必要があります。
VisualStudio2008 を起動する前に、スタートメニューから
Visual Studio Registration → Windows SDK Configuration Tool
を起動して SDK を切り替えておきます。
include パスの順番には注意が必要です。
Tools → Options → Projects and Solutions → VC++ Directories
Win32 / Include files
$(DXSDK_DIR)\include よりも上に $(WindowsSdkDir)\include が来るようにします。
DirectX SDK の install で直接 C:\Program Files ~とパスが書き込まれている
ことがあるので、その行は削除して $(DXSDK_DIR)include に置き換えておくことを
お勧めします。
lib も同じようにします。
● Vista で Direct2D が動く
Windows7 SDK beta があれば Direct2D のプログラムを作ることが出来ます。
実際に走らせるには Windows7 beta をインストールした PC が必要です。
Direct2D は結局 Direct3D10.1 を呼び出しているだけのはず。
Direct3D10.1 で下位 GPU もなぜか動くことがわかったので、もしやと思って
Windows7 beta から d2d1.dll だけ vista に持ってきたら拍子抜けするほどあっさり
動作しました。
とりあえず build したプログラムと同じ場所に d2d1.dll と DWrite.dll だけ置いておけば
Vsita でも Direct2D のプログラムを作って走らせることが出来るようです。
関連エントリ
・Direct2D その(2) インターフェース
・Direct2D と Direct3D10.1 の下位互換
DXT(S3TC/BC) 等、テクスチャ圧縮は基本的に 4x4 pixel 単位で行います。
DXT1~5 (BC1~3) では代表色 2色を RGB565 で格納し、その補間値
3~4 level に対して 2bit の index を持ちます。
これを 4x4 = 16 pixel 分格納します。
16pixel 分の情報が 64bit で収まります。
1pixel あたり 2bit の選択しか出来ないので、4x4 block 単位で最大
4種類のカラー値に変換していることになります。
これはその他のテクスチャ圧縮フォーマットでもほぼ同等の制約です。
色変化の差が激しい画像など、4種類のカラー値で表現しきれない場合は
ブロックノイズとして目立つことになります。
これらの欠点を無くするために、後発のフォーマットでは様々な工夫が
行われています。
高画質化のための方針の一つがモード分岐です。
対象とする画像に応じてブロック単位に圧縮アルゴリズムそのものを
変えてしまうわけです。
特に、以前解説した BC6H/BC7 (OpenGL では BPTC) 圧縮フォーマットに
至っては、8または 14 通りものモード分岐が存在しています。
block 分割 mode を含めれば種類はもっと多くなります。
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
Mobile 系 GPU 、Android 等で使われている ETC1 では、各ブロックをさらに
4x2 のサブブロックに分割します。
各ブロック単位で代表色 1色 + 4段階補間が可能なので、4x4 単位で 8種類の
カラー値を持つことができます。
その代わり色解像度は 4x2 ブロック単位となるため、元画像よりもおよそ
1/8 に低下します。分割方向は block 単位で縦横選べますが、YUV のように
色情報を 4x2 block で共有しているようなものです。
そのため色変化が激しい画像では 4x2block の大きなノイズが生じます。
特にはっきりとした 2色の境界線では ETC1 の劣化が目立ちます。
DXT1 の方がずっと綺麗に出ます。 (2012/08/23追記 実例を載せました)
OpenGL ES 3.0 ではこれらの欠点を克服するために ETC2 が採用されました。
● ETC2 の改善点
(1) ETC1 の block ノイズが出やすいケースを軽減、高画質化
(2) DXT5/3DC-X/3DC-XY (BC3/BC4/BC5) のようなフォーマットバリエーションの追加
(1) 高画質化
ETC1 に対するモード分岐の追加です。
もともとカラー値には 2通りの格納方法がありましたが合計 5 mode 分岐に
増えました。
ETC2 の特徴の一つが ETC1 の上位互換で、bit フォーマットは完全に
互換性があります。
ETC1 の差分 mode のオーバーフローを利用して、存在し得ない bit 並び
になたっときに ETC2 とみなします。
ETC1 の上 2つは 4x2 サブブロック分割があり、分割方向を選ぶことができます。
ETC2 の T-Mode/H-Mode はサブブロック分割がありません。
そのため 4x4 block 内で 4値の選択となります。
ETC2 の最後の Planar は 4x4 block で 3色を直に格納します。
index がなく単純なグラデーションのみとなります。
ポリゴンの頂点カラーをグーロー補間するような感じです。
まだ実際に画像で試していませんが、サブブロック分割が無くなったため
斜め線など 2色の境界は表現できるようになっているようです。
(2) フォーマットバリエーションの追加
ETC1 は RGB しか格納できず alpha カラー値が含まれる場合 GPU
固有の圧縮フォーマットを用いる必要がありました。
ETC2 は 単チャンネル圧縮フォーマット EAC との組み合わせで DXT5(BC3)
のような使い方ができます。
DXT1 は Alpha 1bit のあり無しを 4x4 block 単位で選ぶことができました。
ETC2 では別フォーマット扱いとなります。
ETC1 互換モードの mode 選択 bit を流用するため ETC1 時に 1 mode 減ります。
EAC は BC4/5 同様 pixel あたり 3bit の情報を持ち、8段階選択できます。
ただしベース値を元に 11bit 相当の補間が可能となっているようです。
●その他の新テクスチャフォーマット
・PVRTC2 (PVRTCII)
ETC2 同様に PVRTC とほぼ互換性を保ちつつ、ほとんど使われることが
なかった bit を利用してモード分岐が追加されています。
4 mode あるようですが詳しい構造はわかりません。
すでにツールがあるので比較できますが、従来苦手だった画像でも良くなっており
かなり画質向上が見込めるようです。
・ASTC
ETC2 とそのバリエーションによって OpenGL ES 3.0 では S3TC(DXT) BC1~5
相当をカバーできるようになりました。
ASTC はさらにその上を狙ったもので、HDR対応などを考えるとおそらく
BC6H/BC7 (BPTC) の対抗フォーマットではないかと思います。
圧縮サイズの単位が 128bit なのも BC6H/BC7 (BPTC) と同じです。
block 圧縮のテクスチャフォーマットの方向性の一つがモード分岐の
増加ですが、ASTC はさらにブロックサイズが可変となりました。
PVRTC の 2bpp mode は、通常 4x4 サイズであるブロックを 8x4 に拡張する
ことで実現しています。8x4 = 32block では index が足りないため、
実質 32pixel で 2色の選択になります。
ASTC では可変 block サイズを汎用化しており、mode 分岐だけでなく
より効率の良い 8bpp 未満の圧縮率も同時に実現しているようです。
後ほどもう少し詳しく調べてみたいと思っています。
(2012/08/23追記 :OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC)
関連エントリ
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
・OpenGL の圧縮テクスチャ (3) BPTC, BC6H/BC7
・OpenGL の圧縮テクスチャ (2) 法線圧縮
・Android OpenGL ES 2.0 の圧縮テクスチャ
DXT1~5 (BC1~3) では代表色 2色を RGB565 で格納し、その補間値
3~4 level に対して 2bit の index を持ちます。
これを 4x4 = 16 pixel 分格納します。
代表色 2色 16bit x 2色 = 32bit Index 2bit x 16pixel = 32bit
16pixel 分の情報が 64bit で収まります。
DXT1(BC1) 64bit 4bpp RGB565 256bit 16bpp RGB888 384bit 24bpp
1pixel あたり 2bit の選択しか出来ないので、4x4 block 単位で最大
4種類のカラー値に変換していることになります。
これはその他のテクスチャ圧縮フォーマットでもほぼ同等の制約です。
色変化の差が激しい画像など、4種類のカラー値で表現しきれない場合は
ブロックノイズとして目立つことになります。
これらの欠点を無くするために、後発のフォーマットでは様々な工夫が
行われています。
高画質化のための方針の一つがモード分岐です。
対象とする画像に応じてブロック単位に圧縮アルゴリズムそのものを
変えてしまうわけです。
特に、以前解説した BC6H/BC7 (OpenGL では BPTC) 圧縮フォーマットに
至っては、8または 14 通りものモード分岐が存在しています。
block 分割 mode を含めれば種類はもっと多くなります。
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
Mobile 系 GPU 、Android 等で使われている ETC1 では、各ブロックをさらに
4x2 のサブブロックに分割します。
各ブロック単位で代表色 1色 + 4段階補間が可能なので、4x4 単位で 8種類の
カラー値を持つことができます。
その代わり色解像度は 4x2 ブロック単位となるため、元画像よりもおよそ
1/8 に低下します。分割方向は block 単位で縦横選べますが、YUV のように
色情報を 4x2 block で共有しているようなものです。
そのため色変化が激しい画像では 4x2block の大きなノイズが生じます。
特にはっきりとした 2色の境界線では ETC1 の劣化が目立ちます。
DXT1 の方がずっと綺麗に出ます。 (2012/08/23追記 実例を載せました)
OpenGL ES 3.0 ではこれらの欠点を克服するために ETC2 が採用されました。
● ETC2 の改善点
(1) ETC1 の block ノイズが出やすいケースを軽減、高画質化
(2) DXT5/3DC-X/3DC-XY (BC3/BC4/BC5) のようなフォーマットバリエーションの追加
(1) 高画質化
ETC1 に対するモード分岐の追加です。
もともとカラー値には 2通りの格納方法がありましたが合計 5 mode 分岐に
増えました。
ETC2 の特徴の一つが ETC1 の上位互換で、bit フォーマットは完全に
互換性があります。
ETC1 の差分 mode のオーバーフローを利用して、存在し得ない bit 並び
になたっときに ETC2 とみなします。
ETC1 444(x4 LV) + 444(x4 LV) table 3+3 = 30+32= 62bit (4x2 block) ETC1 555(x4 LV) + diff 333(x4 LV) table 3+3 = 30+32= 62bit (4x2 block) ETC2 444(x3 LV) + 444 dist 3 = 27+32= 59bit (T-Mode) ETC2 444(x2 LV) + 444(x2 LV) dist 3 = 27+32= 59bit (H-Mode) ETC2 676 + 676 + 676 = 57bit (Planar)
ETC1 の上 2つは 4x2 サブブロック分割があり、分割方向を選ぶことができます。
ETC2 の T-Mode/H-Mode はサブブロック分割がありません。
そのため 4x4 block 内で 4値の選択となります。
ETC2 の最後の Planar は 4x4 block で 3色を直に格納します。
index がなく単純なグラデーションのみとなります。
ポリゴンの頂点カラーをグーロー補間するような感じです。
まだ実際に画像で試していませんが、サブブロック分割が無くなったため
斜め線など 2色の境界は表現できるようになっているようです。
(2) フォーマットバリエーションの追加
ETC1 は RGB しか格納できず alpha カラー値が含まれる場合 GPU
固有の圧縮フォーマットを用いる必要がありました。
ETC2 は 単チャンネル圧縮フォーマット EAC との組み合わせで DXT5(BC3)
のような使い方ができます。
ETC2 RGB 4bpp DXT1/BC1 ETC2 RGB + Alpha 1bit 4bpp DXT1/BC1 ETC2 RGB + Alpha EAC 8bpp DXT5/BC3 EAC 4bpp 3DC-X/BC4 EAC + EAC 8bpp 3DC-XY/BC5
DXT1 は Alpha 1bit のあり無しを 4x4 block 単位で選ぶことができました。
ETC2 では別フォーマット扱いとなります。
ETC1 互換モードの mode 選択 bit を流用するため ETC1 時に 1 mode 減ります。
EAC は BC4/5 同様 pixel あたり 3bit の情報を持ち、8段階選択できます。
ただしベース値を元に 11bit 相当の補間が可能となっているようです。
●その他の新テクスチャフォーマット
・PVRTC2 (PVRTCII)
ETC2 同様に PVRTC とほぼ互換性を保ちつつ、ほとんど使われることが
なかった bit を利用してモード分岐が追加されています。
4 mode あるようですが詳しい構造はわかりません。
すでにツールがあるので比較できますが、従来苦手だった画像でも良くなっており
かなり画質向上が見込めるようです。
・ASTC
ETC2 とそのバリエーションによって OpenGL ES 3.0 では S3TC(DXT) BC1~5
相当をカバーできるようになりました。
ASTC はさらにその上を狙ったもので、
BC6H/BC7 (BPTC) の対抗フォーマットではないかと思います。
圧縮サイズの単位が 128bit なのも BC6H/BC7 (BPTC) と同じです。
block 圧縮のテクスチャフォーマットの方向性の一つがモード分岐の
増加ですが、ASTC はさらにブロックサイズが可変となりました。
PVRTC の 2bpp mode は、通常 4x4 サイズであるブロックを 8x4 に拡張する
ことで実現しています。8x4 = 32block では index が足りないため、
実質 32pixel で 2色の選択になります。
ASTC では可変 block サイズを汎用化しており、mode 分岐だけでなく
より効率の良い 8bpp 未満の圧縮率も同時に実現しているようです。
後ほどもう少し詳しく調べてみたいと思っています。
(2012/08/23追記 :OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC)
関連エントリ
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
・OpenGL の圧縮テクスチャ (3) BPTC, BC6H/BC7
・OpenGL の圧縮テクスチャ (2) 法線圧縮
・Android OpenGL ES 2.0 の圧縮テクスチャ
2009/06/12
Direct3D11/DirectX11 ComputeShader 4.0 を使う
DirectX11 の ComputeShader が実際に動くようになりました。
・Direct3D11 GeForce driver 186.08 beta
ComputeShader 4.0 は FeatureLevel 10.0。つまり 1世代前の GPU で動作します。
cs4.0/4.1 の存在自体は November 2008 SDK でも確認できました。
しかしながら実際に使うためにはドライバの対応を待つ必要があったわけです。
本来の ComputeShader (cs5.0) の仕様に対して cs4.0 はサブセットとなります。
機能制限のある cs4.0 は、ぱっと見 PixelShader とほとんど違わないし
どこにメリットがあるのかあまりわからないかもしれません。
とりあえず目に付いた ComputeShader4.0 の利点は次の通り。
・シェーダーがバッファに書き込みできる
・共有メモリへアクセスできる
描画パイプラインに組み込まれたシェーダーの場合、ストリームとして動作するため
出力位置は固定でした。
ComputeShader は戻り値を持たず、代わりに UnorderedAccessView を通して
リソースの任意の位置にデータを書き込むことができます。
共有メモリはスレッド間でデータの受け渡しができるプロセッサ内部のローカルメモリです。
高速アクセス可能で他のスレッドが書き込んだ値を参照することもできます。
VertexShader 等を通さずに直接シェーダーのみ実行できるのも ComputeShader
の特徴となります。
●実行とデータの受け渡し
ComputeShader を実行する命令は Dispatch() です。
この例では入力が ShaderResourceView (SRV)、出力は
UnroderedAccessView (UAV) です。
入力データは何らかの形で SRV のリソースに書き込んでおきます。
ConstantBuffer や Texture へ書き込む手順と変わらないので難しくありません。
実行結果は UAV で受け取ります。
UAV のリソースは RenderTarget 同様 GPU が書き込むため D3D11_USAGE_DEFAULT
を指定しておきます。
デバッグ時など、結果を何らかの形で CPU で受け取りたい場合は
D3D11_USAGE_STAGING のバッファにコピーしなければなりません。
STAGING リソースから読み込む場合 Map() を使いますが、STAGING への Map は
ChildContext で実行することができませんでした。
STAGING は直接メモリアクセスしているのに、コマンドをバッファにためるだけでは
意味がないので当然かもしれません。
スレッド対応のためにすべて Deferred Context 化している場合は注意が必要です。
Immediate Context を使います。
(1) SRV リソースへ書き込み
・UpdateSubresource() → USAGE_DEFAULT
・Map() → USAGE_STAGING → CopyResource() → USAGE_DEFAULT
・Map() → USAGE_DYNAMIC
(2) 実行 Dispatch()
(3) UAV から結果受け取り
・USAGE_DEFAULT → CopyResource() → USAGE_STAGING → Map()
このように、ComputeShader の場合 CPU とのデータやりとりも一般のシェーダーと
同じ手順が必要になります。
CUDA とか ATI Stream (CAL) より若干手間がいるかもしれません。
ComputeShader 4.x の場合、UAV は RAW または Structured Buffer のみ使用できます。
同時に与えられる UAV も 1つだけです。
● ComputeShader の実行回数と ID
PixelShader なら 2次元の面積で描画するピクセル数が決定します。
このピクセルの数だけ Shader を実行することになります。
ComputeShader の場合 3次元の体積で実行するスレッドの数を指定できます。
例えば XYZ= ( 2, 3, 1 ) の場合、2 x 3 x 1 = 6 スレッド走ります。
各スレッドは自分がどの座標に対応しているのか 3次元の id 番号を
受け取ることができるわけです。
実行するスレッドの個数の指定は二カ所で行います。
1. Dispatch() の引数
2. ComputeShader のアトリビュート numthreads 指定
両者の指定は似ていますが別物です。
6次元の id 値を持っているといえるかもしれません。
・1グループ内のスレッド実行回数の定義
[numthreads()] で指定するのは 1グループ内で実行するスレッドの数です。
シェーダーのコンパイル時に決定するので static な値となります。
uint3 SV_GroupThreadID // グループ内のスレッド番号
semantic SV_GroupThreadID はグループ内の id 番号を受け取ることができます。
[numthreads(3,2,1)] は次の 6 通り。
int SV_GroupIndex // グループ内の通し番号
SV_GroupIndex はグループ内の連番です。
[numthreads(3,2,1)] の場合 0~5 の連番を受け取ります。
・グループ自体を何回実行するか指定する
Dispatch() の引数は、シェーダーで定義したスレッド Group をさらに何回実行するか
指定します。例えば [numthreads(3,2,1)] を Dispatch( 2, 1, 1 ) で呼び出すと
合計 12スレッドプログラムが走ることになります。
uint3 SV_GroupID // グループの番号
SV_GroupID はグループにつけられた番号です。
例えば Dispatch( 10, 2, 1 ) で呼び出すと (0,0,0)~(9,1,0) まで。
同じグループの中のスレッドはすべて同じ値です。
uint3 SV_DispatchThreadID // 3次元の通し番号
SV_DispatchThreadID は numthreads も Dispatch も区別無く、3次元に展開した
スレッドの通し番号になります。
例えば [numthreads(3,2,1)] を Dispatch( 4, 9, 1 ) で呼び出すと 216スレッド。
(3*4, 2*9, 1*1) の範囲なので (0,0,0)~(11,17,0) までの値を取ります。
[numthreads(3,2,1)] を Dispatch( 2, 1, 1 ) で呼び出した場合の ID 一覧。
ComputeShader4.x の場合スレッド数の 3番目の値は必ず 1 でなければなりません。
つまり PixelShader 同様 2次元の id 指定に制限されます。
● Group
上記のようにスレッドの実行にはグループという概念があります。
プログラムの実行はこのグループ単位に行われているようです。
・スレッド共有メモリは group 毎に確保される
・共有メモリへのアクセスは group 単位で同期できる
●共有メモリ
ComputeShader はローカルな共有メモリにアクセスすることができます。
メモリ空間にマップされるわけではないので Buffer より高速です。
ShaderResource と違い読み書きも可能です。
同一グループ内の他のスレッドと共有できます。他のスレッドでも書き込んだ値を参照可能です。
・レジスタ = スレッド固有
・共有メモリ = グループ内のみ共有
・リソース(Buffer)
Shader プログラム内で groupshared 宣言すると共有メモリになります。
ComputeShader 4.x ではいくつか制限があります。
容量は 16KB までです。
また共有メモリの宣言はグループ内のスレッド数と同数でなければならず、
書き込めるのは自分の SV_GroupIndex に対応するメモリだけ。
宣言できる共有メモリも1つだけです。
その代わり Structured のように構造体を与えることが可能です。
groupshared 指定は、アセンブラコードだと下記の宣言になります。
g0 が共有メモリです。
●同期
ComputeShader 4.x の場合 Interlock 系の Atomic なオペレーション命令は
ありませんが、メモリアクセスに対する Barrier 同期は使えるようです。
GeForce 用ドライバ 186.08 beta ではこれらの命令が動作しませんでした。
命令のコンパイル自体は可能だし DebugLayer も無言だったので、ドライバがまだ
対応していないだけかもしれません。
Reference Driver を使うと Shader4.0 でも動作しました。
アセンブラでは sync 命令が挿入されています。
関連エントリ
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 GeForce driver 186.08 beta
ComputeShader 4.0 は FeatureLevel 10.0。つまり 1世代前の GPU で動作します。
cs4.0/4.1 の存在自体は November 2008 SDK でも確認できました。
しかしながら実際に使うためにはドライバの対応を待つ必要があったわけです。
本来の ComputeShader (cs5.0) の仕様に対して cs4.0 はサブセットとなります。
機能制限のある cs4.0 は、ぱっと見 PixelShader とほとんど違わないし
どこにメリットがあるのかあまりわからないかもしれません。
とりあえず目に付いた ComputeShader4.0 の利点は次の通り。
・シェーダーがバッファに書き込みできる
・共有メモリへアクセスできる
描画パイプラインに組み込まれたシェーダーの場合、ストリームとして動作するため
出力位置は固定でした。
ComputeShader は戻り値を持たず、代わりに UnorderedAccessView を通して
リソースの任意の位置にデータを書き込むことができます。
共有メモリはスレッド間でデータの受け渡しができるプロセッサ内部のローカルメモリです。
高速アクセス可能で他のスレッドが書き込んだ値を参照することもできます。
VertexShader 等を通さずに直接シェーダーのみ実行できるのも ComputeShader
の特徴となります。
●実行とデータの受け渡し
ComputeShader を実行する命令は Dispatch() です。
iDeviceContext->CSSetShader( iCS, NULL, 0 ); UINT v= 0; iDeviceContext->CSSetUnorderedAccessViews( 0, 1, &iOutputUAView, &v ); iDeviceContext->CSSetShaderResources( 0, 1, &iInputView ); iDeviceContext->Dispatch( 1, 1, 1 );
この例では入力が ShaderResourceView (SRV)、出力は
UnroderedAccessView (UAV) です。
入力データは何らかの形で SRV のリソースに書き込んでおきます。
ConstantBuffer や Texture へ書き込む手順と変わらないので難しくありません。
実行結果は UAV で受け取ります。
UAV のリソースは RenderTarget 同様 GPU が書き込むため D3D11_USAGE_DEFAULT
を指定しておきます。
デバッグ時など、結果を何らかの形で CPU で受け取りたい場合は
D3D11_USAGE_STAGING のバッファにコピーしなければなりません。
STAGING リソースから読み込む場合 Map() を使いますが、STAGING への Map は
ChildContext で実行することができませんでした。
STAGING は直接メモリアクセスしているのに、コマンドをバッファにためるだけでは
意味がないので当然かもしれません。
スレッド対応のためにすべて Deferred Context 化している場合は注意が必要です。
Immediate Context を使います。
(1) SRV リソースへ書き込み
・UpdateSubresource() → USAGE_DEFAULT
・Map() → USAGE_STAGING → CopyResource() → USAGE_DEFAULT
・Map() → USAGE_DYNAMIC
(2) 実行 Dispatch()
(3) UAV から結果受け取り
・USAGE_DEFAULT → CopyResource() → USAGE_STAGING → Map()
このように、ComputeShader の場合 CPU とのデータやりとりも一般のシェーダーと
同じ手順が必要になります。
CUDA とか ATI Stream (CAL) より若干手間がいるかもしれません。
ComputeShader 4.x の場合、UAV は RAW または Structured Buffer のみ使用できます。
同時に与えられる UAV も 1つだけです。
● ComputeShader の実行回数と ID
PixelShader なら 2次元の面積で描画するピクセル数が決定します。
このピクセルの数だけ Shader を実行することになります。
ComputeShader の場合 3次元の体積で実行するスレッドの数を指定できます。
例えば XYZ= ( 2, 3, 1 ) の場合、2 x 3 x 1 = 6 スレッド走ります。
各スレッドは自分がどの座標に対応しているのか 3次元の id 番号を
受け取ることができるわけです。
実行するスレッドの個数の指定は二カ所で行います。
1. Dispatch() の引数
2. ComputeShader のアトリビュート numthreads 指定
[numthreads(3,2,1)] // ←これ void cmain1( uint3 did : SV_DispatchThreadID, uint3 gid: SV_GroupThreadID, uint3 group: SV_GroupID, uint gindex : SV_GroupIndex ) { int index= did.x + did.y * 10; ~
両者の指定は似ていますが別物です。
6次元の id 値を持っているといえるかもしれません。
・1グループ内のスレッド実行回数の定義
[numthreads()] で指定するのは 1グループ内で実行するスレッドの数です。
シェーダーのコンパイル時に決定するので static な値となります。
uint3 SV_GroupThreadID // グループ内のスレッド番号
semantic SV_GroupThreadID はグループ内の id 番号を受け取ることができます。
[numthreads(3,2,1)] は次の 6 通り。
(0,0,0) (1,0,0) (2,0,0) (0,1,0), (1,1,0), (2,1,0)
int SV_GroupIndex // グループ内の通し番号
SV_GroupIndex はグループ内の連番です。
[numthreads(3,2,1)] の場合 0~5 の連番を受け取ります。
・グループ自体を何回実行するか指定する
Dispatch() の引数は、シェーダーで定義したスレッド Group をさらに何回実行するか
指定します。例えば [numthreads(3,2,1)] を Dispatch( 2, 1, 1 ) で呼び出すと
合計 12スレッドプログラムが走ることになります。
uint3 SV_GroupID // グループの番号
SV_GroupID はグループにつけられた番号です。
例えば Dispatch( 10, 2, 1 ) で呼び出すと (0,0,0)~(9,1,0) まで。
同じグループの中のスレッドはすべて同じ値です。
uint3 SV_DispatchThreadID // 3次元の通し番号
SV_DispatchThreadID は numthreads も Dispatch も区別無く、3次元に展開した
スレッドの通し番号になります。
例えば [numthreads(3,2,1)] を Dispatch( 4, 9, 1 ) で呼び出すと 216スレッド。
(3*4, 2*9, 1*1) の範囲なので (0,0,0)~(11,17,0) までの値を取ります。
[numthreads(3,2,1)] を Dispatch( 2, 1, 1 ) で呼び出した場合の ID 一覧。
SV_GroupID SV_GroupThreadID SV_GroupIndex SV_DispatchThreadID (0,0,0) (0,0,0) 0 (0,0,0) (0,0,0) (1,0,0) 1 (1,0,0) (0,0,0) (2,0,0) 2 (2,0,0) (0,0,0) (0,1,0) 3 (0,1,0) (0,0,0) (1,1,0) 4 (1,1,0) (0,0,0) (2,1,0) 5 (2,1,0) (1,0,0) (0,0,0) 0 (3,0,0) (1,0,0) (1,0,0) 1 (4,0,0) (1,0,0) (2,0,0) 2 (5,0,0) (1,0,0) (0,1,0) 3 (3,1,0) (1,0,0) (1,1,0) 4 (4,1,0) (1,0,0) (2,1,0) 5 (5,1,0)
ComputeShader4.x の場合スレッド数の 3番目の値は必ず 1 でなければなりません。
つまり PixelShader 同様 2次元の id 指定に制限されます。
● Group
上記のようにスレッドの実行にはグループという概念があります。
プログラムの実行はこのグループ単位に行われているようです。
・スレッド共有メモリは group 毎に確保される
・共有メモリへのアクセスは group 単位で同期できる
●共有メモリ
ComputeShader はローカルな共有メモリにアクセスすることができます。
メモリ空間にマップされるわけではないので Buffer より高速です。
ShaderResource と違い読み書きも可能です。
同一グループ内の他のスレッドと共有できます。他のスレッドでも書き込んだ値を参照可能です。
・レジスタ = スレッド固有
・共有メモリ = グループ内のみ共有
・リソース(Buffer)
Shader プログラム内で groupshared 宣言すると共有メモリになります。
ComputeShader 4.x ではいくつか制限があります。
容量は 16KB までです。
また共有メモリの宣言はグループ内のスレッド数と同数でなければならず、
書き込めるのは自分の SV_GroupIndex に対応するメモリだけ。
宣言できる共有メモリも1つだけです。
その代わり Structured のように構造体を与えることが可能です。
struct SharedMem { float2 fl; uint id; }; groupshared SharedMem buf[6]; // 3x2 [numthreads(3,2,1)] void cmain( uint3 did : SV_DispatchThreadID, uint3 gid: SV_GroupThreadID, uint3 group: SV_GroupID, uint gindex : SV_GroupIndex ) { buf[ gindex ].fl.x= gindex; ~
groupshared 指定は、アセンブラコードだと下記の宣言になります。
g0 が共有メモリです。
dcl_tgsm_structured g0, 32, 6
●同期
ComputeShader 4.x の場合 Interlock 系の Atomic なオペレーション命令は
ありませんが、メモリアクセスに対する Barrier 同期は使えるようです。
GroupMemoryBarrierWithGroupSync(); GroupMemoryBarrier(); AllMemoryBarrierWithGroupSync(); AllMemoryBarrier();
GeForce 用ドライバ 186.08 beta ではこれらの命令が動作しませんでした。
命令のコンパイル自体は可能だし DebugLayer も無言だったので、ドライバがまだ
対応していないだけかもしれません。
Reference Driver を使うと Shader4.0 でも動作しました。
アセンブラでは sync 命令が挿入されています。
sync_g GroupMemoryBarrier() sync_g_t GroupMemoryBarrierWithGroupSync() sync_uglobal_g AllMemoryBarrier() sync_uglobal_g_t AllMemoryBarrierWithGroupSync()
関連エントリ
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
2011/05/16
DirectX 11 Driver Command Lists
NVIDIA GeForce GTX 460 でドライバを v270.61 に入れ替えたら
ついに! Driver Command Lists が Yes になっていました。
Direct3D 11 が出てから1年半、ようやくフル機能使えるようになりました。
関連エントリ
・OpenGL 4.1 GeForce GTX 460
・DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
・Direct3D11/DirectX11 GeForce の ComputeShader とドライバ
ついに! Driver Command Lists が Yes になっていました。
Direct3D 11 が出てから1年半、ようやくフル機能使えるようになりました。
関連エントリ
・OpenGL 4.1 GeForce GTX 460
・DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
・Direct3D11/DirectX11 GeForce の ComputeShader とドライバ
●Tessellator のタイプ
Tessellator の出力には 3種類のタイプがあります。
2008-11-17追記:間違いを修正しました。上記赤字と、tri/quad では line を出力できませんでした。
quad, tri はそれぞれ四角パッチ、三角パッチを意味しています。
isoline は面を均等に割った 2次元頂点を出力するようです。
TessFactor[1] が 1.0 の場合、線分の分割に相当します。
例えば isoline で、TessFactor=8,4 を与えると下記の点を出力します。

Tessellator が出力するプリミティブの形状は次の4種類です。
これらは Hull Shader のアトリビュートで指定します。
出力トポロジの影響を直接受けるのは Geometry Shader 以降ですが、その設定も
Domain Shader ではなく HullShader が行うので注意。
ちなみに DomainShader は、プリミティブの形状 (outputtopology) を判別する
手段がありません。
DomainShader は TessFactor を参照できるため、上の domain のみ指定する必要
があります。
isoline では triangle を出力できません。
line を指定すると、単なる横線が複数出力されました。
●TessFactor の種類
partitioning は TessFactor による分割方法を指定します。
fractional だと、TessFactor が実数値の場合間を補間します。
LOD のつながりがよりスムーズになると考えられます。
outputtopology も、partitioning もマニュアルに書いてあることと違います。
まだまだ間違いが多いようです。
●アウトラインフォントの描画
テセレータパイプラインは、HullShader と DomainShader のおかげでかなり自由度が
高くなっています。固定機能の制限は小さく、さまざまな応用が考えられます。
以前「3D だけでなく、ベクタフォントを GPU で直接描画するなど 2D 面でも応用できる
かもしれません。」 などと書いてしまったので実際にやってみました。


拡大とワイヤー表示は次の通り


実際の補間は DomainShader が行っているため、分割したポリゴンのエッジだけ
細分化することは思ったより簡単にできそうです。
quiad や triangle で特定のエッジだけ割っても良いのですが、内部に出来た頂点が
無駄になるし、割ったエッジに影響が出ないよう一点に集めないといけないので
少々複雑です。
isoline があるので、テセレータの段階では輪郭をそのまま割るだけにします。
ポリゴン化は GeometryShader で行います。
簡単にするため、データは前処理で専用のものを用意します。
3次ベジェ のアウトラインを使っています。

各区間、A-B や B-C はそれぞれ 2点の頂点座標(アンカー)+ハンドル2点 で表現
できます。各区間を 4点の制御座標でデータ化します。
このとき隣接するアンカーポイント(頂点)は共有可能です。
これをそのまま D3D11_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST
で渡し、domain("isoline")、outputtopology("line") で描画すれば容易にアウトラインの
線描画ができます。DomainShader でベジェカーブを求めるだけです。
フォントのアウトラインそのままで追加情報は不要です。
塗りつぶしは少々手間がかかります。上図の X, Y のように前処理で描画用の
追加点が必要です。
A-B は A-X-B の 3角形、B-C は B-X-Y-C の 4角形を描画します。
処理を簡単にするため、A-X-B は X が2頂点重なってると見なしてすべて四角形で行います。
これらの追加頂点を分割面 (A-X-B や B-X-Y-C) 毎に保持し、GeometryShader まで
情報が届くようにします。
必要な座標は 2次元 x,y のみです。
float4 にすると z,w にもう 1つ座標を入れられます。
アンカーポイントは共有される可能性があるため、面の情報はハンドル(制御点)側の
頂点 zw に入れます。
頂点フォーマットは単純な float4 のみ
VertexShader はデータにスケーリング補正してるだけでそのまま流します。
HullShader もテセレータの設定のみ。
分割は線分で行うので domain("isoline") を使い、GeometryShader で 2点のつながり
が必要なので出力は outputtopology("line")。
コントロールポイントはそのままスルーで DomainShader に任せます。
DomainShader で分割点のベジェ補間を行い、GeometryShader に渡すための頂点を
構築します。
追加点は 2番目と 3番目の zw に入っているので、それらも取り出して頂点に入れます。
uv は Geometry Shader で線分の最後かどうか判断するために使います。
GeometryShader は頂点情報から Triangle を作ります。
uv で判断しているのは最後だけ 1ポリゴン追加する必要があるためです。
下図でいう 4 番。

描画自体は簡単にできました。
元となるデータを準備する方が大変です。
中心に位置する追加点を求める何らかの手段が必要となりそうです。
関連エントリ
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
Tessellator の出力には 3種類のタイプがあります。
[domain(type)]: isoline TessFactor[2] 出力 point line tri TessFactor[3] InsideTessFactor[1] 出力 pointlinetriangle quad TessFactor[4] InsideTessFactor[2] 出力 pointlinetriangle
2008-11-17追記:間違いを修正しました。上記赤字と、tri/quad では line を出力できませんでした。
quad, tri はそれぞれ四角パッチ、三角パッチを意味しています。
isoline は面を均等に割った 2次元頂点を出力するようです。
TessFactor[1] が 1.0 の場合、線分の分割に相当します。
例えば isoline で、TessFactor=8,4 を与えると下記の点を出力します。

Tessellator が出力するプリミティブの形状は次の4種類です。
[outputtopology(type)]: point line triangle_cw triangle_ccw
これらは Hull Shader のアトリビュートで指定します。
出力トポロジの影響を直接受けるのは Geometry Shader 以降ですが、その設定も
Domain Shader ではなく HullShader が行うので注意。
ちなみに DomainShader は、プリミティブの形状 (outputtopology) を判別する
手段がありません。
DomainShader は TessFactor を参照できるため、上の domain のみ指定する必要
があります。
isoline では triangle を出力できません。
line を指定すると、単なる横線が複数出力されました。
●TessFactor の種類
partitioning は TessFactor による分割方法を指定します。
[partitioning(type)]: integer fractional_even fractional_odd pow2
fractional だと、TessFactor が実数値の場合間を補間します。
LOD のつながりがよりスムーズになると考えられます。
outputtopology も、partitioning もマニュアルに書いてあることと違います。
まだまだ間違いが多いようです。
●アウトラインフォントの描画
テセレータパイプラインは、HullShader と DomainShader のおかげでかなり自由度が
高くなっています。固定機能の制限は小さく、さまざまな応用が考えられます。
以前「3D だけでなく、ベクタフォントを GPU で直接描画するなど 2D 面でも応用できる
かもしれません。」 などと書いてしまったので実際にやってみました。


拡大とワイヤー表示は次の通り


実際の補間は DomainShader が行っているため、分割したポリゴンのエッジだけ
細分化することは思ったより簡単にできそうです。
quiad や triangle で特定のエッジだけ割っても良いのですが、内部に出来た頂点が
無駄になるし、割ったエッジに影響が出ないよう一点に集めないといけないので
少々複雑です。
isoline があるので、テセレータの段階では輪郭をそのまま割るだけにします。
ポリゴン化は GeometryShader で行います。
簡単にするため、データは前処理で専用のものを用意します。
3次ベジェ のアウトラインを使っています。

各区間、A-B や B-C はそれぞれ 2点の頂点座標(アンカー)+ハンドル2点 で表現
できます。各区間を 4点の制御座標でデータ化します。
このとき隣接するアンカーポイント(頂点)は共有可能です。
A-B : 頂点A, 制御点A0, 制御点A1, 頂点B B-C : 頂点B, 制御点B0, 制御点B1, 頂点C
これをそのまま D3D11_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST
で渡し、domain("isoline")、outputtopology("line") で描画すれば容易にアウトラインの
線描画ができます。DomainShader でベジェカーブを求めるだけです。
フォントのアウトラインそのままで追加情報は不要です。
塗りつぶしは少々手間がかかります。上図の X, Y のように前処理で描画用の
追加点が必要です。
A-B は A-X-B の 3角形、B-C は B-X-Y-C の 4角形を描画します。
処理を簡単にするため、A-X-B は X が2頂点重なってると見なしてすべて四角形で行います。
これらの追加頂点を分割面 (A-X-B や B-X-Y-C) 毎に保持し、GeometryShader まで
情報が届くようにします。
必要な座標は 2次元 x,y のみです。
float4 にすると z,w にもう 1つ座標を入れられます。
アンカーポイントは共有される可能性があるため、面の情報はハンドル(制御点)側の
頂点 zw に入れます。
B-C の例、4頂点 pointB.xy = 頂点B pointB.zw = 未使用 ctrlB0.xy = 制御点B0 ctrlB0.zw = 追加の頂点、追加点X ctrlB1.xy = 制御点B1 ctrlB1.zw = 追加の頂点、追加点Y pointC.xy = 頂点C pointC.zw = 未使用
頂点フォーマットは単純な float4 のみ
D3D11_INPUT_ELEMENT_DESC ldesc[]= { { "VPOS", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; iDevice->CreateInputLayout( ldesc, 1, codeblob-
VertexShader はデータにスケーリング補正してるだけでそのまま流します。
// vs.hlsl struct VS_INPUT { float4 vPos : VPOS; }; float4 main( VS_INPUT idata ) : VSPOS { float4 pos= idata.vPos; const float SCALE= 0.08; const float2 CENTER= float2(137,212); pos-= CENTER.xyxy; pos*= SCALE.xxxx; return pos; }
HullShader もテセレータの設定のみ。
分割は線分で行うので domain("isoline") を使い、GeometryShader で 2点のつながり
が必要なので出力は outputtopology("line")。
コントロールポイントはそのままスルーで DomainShader に任せます。
// hs.hlsl struct VS_OUTPUT { float4 vPos : VSPOS; }; struct HS_DATA { float Edge[2] : SV_TessFactor; }; HS_DATA func() { HS_DATA pdata; pdata.Edge[0]= 8.0; // ここで分割数の決定、値が大きいとなめらか pdata.Edge[1]= 1.0; return pdata; } [domain("isoline")] [partitioning("integer")] [outputtopology("line")] [outputcontrolpoints(4)] [patchconstantfunc("func")] float4 main( InputPatch<VS_OUTPUT,4> ip, uint id : SV_OutputControlPointID ) : TSPOS { return ip[id].vPos; }
DomainShader で分割点のベジェ補間を行い、GeometryShader に渡すための頂点を
構築します。
追加点は 2番目と 3番目の zw に入っているので、それらも取り出して頂点に入れます。
uv は Geometry Shader で線分の最後かどうか判断するために使います。
// ds.hlsl struct HS_DATA { float Edge[2] : SV_TessFactor; }; struct HS_OUTPUT { float4 vPos : TSPOS; }; cbuffer perFrame { float4x4 WVP; }; float4 Bezier0( float t ) { float t2= 1.0-t; return float4( t2*t2*t2, t2*t2* t*3, t2* t* t*3, t* t* t ); } float2 UVtoPosition( const OutputPatch<HS_OUTPUT,4> p, float2 uv ) { float4 u= Bezier0( uv.x ); return u.x*p[0].vPos.xy +u.y*p[1].vPos.xy +u.z*p[2].vPos.xy +u.w*p[3].vPos.xy; } struct DS_OUTPUT { float4 nPos[2] : NPOS; float uv : UV; float4 vPos : SV_Position; }; [domain("isoline")] DS_OUTPUT main( HS_DATA ip, float2 uv : SV_DomainLocation, const OutputPatch<HS_OUTPUT,4> bpatch ) { DS_OUTPUT dsout; float2 pos= UVtoPosition( bpatch, uv ); dsout.vPos= mul( float4( pos.xy, 0, 1 ), WVP ); // 追加点 2つを各頂点に埋め込む dsout.nPos[0]= mul( float4( bpatch[1].vPos.zw, 0, 1 ), WVP ); dsout.nPos[1]= mul( float4( bpatch[2].vPos.zw, 0, 1 ), WVP ); dsout.uv= uv.x; // Geometry Shader で使う return dsout; }
GeometryShader は頂点情報から Triangle を作ります。
// gs.hlsl struct DS_OUTPUT { float4 nPos[2] : NPOS; float uv : UV; float4 vPos : SV_Position; }; [maxvertexcount(6)] void main( line DS_OUTPUT input[2], inout TriangleStream<DS_OUTPUT> stream ) { stream.Append( input[0] ); stream.Append( input[1] ); DS_OUTPUT dout2= input[0]; dout2.vPos= input[0].nPos[0]; stream.Append( dout2 ); stream.RestartStrip(); if( input[1].uv >= 0.9998f ){ DS_OUTPUT dout3= input[1]; dout3.vPos= input[0].nPos[1]; stream.Append( input[1] ); stream.Append( dout2 ); stream.Append( dout3 ); stream.RestartStrip(); } }
uv で判断しているのは最後だけ 1ポリゴン追加する必要があるためです。
下図でいう 4 番。

描画自体は簡単にできました。
元となるデータを準備する方が大変です。
中心に位置する追加点を求める何らかの手段が必要となりそうです。
関連エントリ
・Direct3D11/DirectX11 (8) テセレータの動作
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver
2009/09/28
GeForce ドライバ 191.03 beta
GeForce のドライバ 191.03 (beta) が出ています。
やっと OpenGL 3.2 と Compute Shader 4.0 の共存が出来るようになりました。
どちらも動いています。
・GEFORCE/ION DRIVER RELEASE 191
この辺 RADEON はまだ対応していませんが、すでに DirectX11 が動く 5870 が発売されました。
ShaderModel とか Compte Shader の対応とか非常に興味あるけど
週末 RADEON HD 5870 はすでに売り切れでした。
関連エントリ
・OpenGL や GLSL の互換性
・Direct3D11/DirectX11 GeForce の ComputeShader とドライバ
やっと OpenGL 3.2 と Compute Shader 4.0 の共存が出来るようになりました。
どちらも動いています。
・GEFORCE/ION DRIVER RELEASE 191
この辺 RADEON はまだ対応していませんが、すでに DirectX11 が動く 5870 が発売されました。
ShaderModel とか Compte Shader の対応とか非常に興味あるけど
週末 RADEON HD 5870 はすでに売り切れでした。
関連エントリ
・OpenGL や GLSL の互換性
・Direct3D11/DirectX11 GeForce の ComputeShader とドライバ
2012/08/21
OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC
DXTC(S3TC)/PVRTC/ETC1/ETC2 など、4x4 block を 64bit 単位で
変換する圧縮テクスチャフォーマットが第一世代なら、
128bit 単位で処理する BC6H/BC7 (BPTC) は第二世代の新しい
圧縮テクスチャフォーマットと言えます。
64bit の枠を超えた余剰 bit を、モード分岐やパーティション分割等
画質を向上させるための様々な情報として用います。
ASTC も同様で、BC6H/BC7(BPTC) と同じように 128bit 単位で圧縮を
行います。その仕組はさらに複雑でより踏み込んだ内容でした。
・128bit 単位
・複雑なモード分岐
・Dual Plane 機能 (Index を 2セット持つ)
・Partition 分割 (カラーセット Endpoint を複数持つ)
これらの特徴は BC6H/BC7(BPTC) と共通のものです。
ASTC にはさらに独自の仕様が多数含まれています。
・可変 Block サイズ (4x4 ~ 12x12)
・最大 4分割 の Partition (BPTC は 3分割まで)
・2,3,5 の 3種類の基数選択
・1~4 component の自由なフォーマット (R,RG,RGB,RGBA)
Khronos の仕様では LDR モードのみ定義されていますが、
ARM のドキュメントでは 3D texture や HDR まで言及されており
より幅広い用途が想定されていたことがわかります。
・KHR_texture_compression_astc_ldr
・ARM Mali developer Center: ASTC Evaluation Codec
bit field は非常に自由で、モードと Endpoint (代表カラー値) が
下位 bit から、Index (Texel Weight) が上位 bit から必要個数並ぶという
可変フォーマットです。
そのため組み合わせ数は膨大で単純なモード数では表現できません。
また 2進数からくる制約が少ないのも大きな特徴の一つです。
例えば block サイズも 5x5 , 6x6 , 10x8 など中途半端なサイズを
選ぶことができます。
Index によるカラーの補間も、DXT 等では 2bit なので 4段階ですが、
ASTC では 3値や 5値等の任意の段階を選ぶことができます。
DXT では 2色の代表カラーを選んでも、4階調だとちょうど中間の値を
生成することができません。画像によっては都合が悪く、敢えて
DXT1 の 3色モード (Alpha 1bit) を用いることがありました。
ASTC は 3 と 5 の基数を用いることができます。
5つの 3階調値を 8bit (3^5 = 243) に格納し、同様に 3つの 5階調値を
7bit (5^3 = 125) に収めます。
これに任意ビット数のスケール値を掛けることで、2 の n 乗に制限されず
下記の値の範囲を格納することができます。
例えば 12 (0~11) 段階をエンコードするなら 5個単位で格納します。
それぞれが 2bit のスケールを持つので、8bit + 5*2bit = 18bit (5個分)
となります。
このようにより自由なエンコードアルゴリズムを用いつつ、かつハードウエアで
実装しやすいようさまざまな工夫が施されています。
関連エントリ
・OpenGL 4.3/ES 3.0 ETC2 Texture 圧縮の仕組み (PVRTC2,ASTC)
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
・OpenGL の圧縮テクスチャ (3) BPTC, BC6H/BC7
・OpenGL の圧縮テクスチャ (2) 法線圧縮
・Android OpenGL ES 2.0 の圧縮テクスチャ
変換する圧縮テクスチャフォーマットが第一世代なら、
128bit 単位で処理する BC6H/BC7 (BPTC) は第二世代の新しい
圧縮テクスチャフォーマットと言えます。
64bit の枠を超えた余剰 bit を、モード分岐やパーティション分割等
画質を向上させるための様々な情報として用います。
ASTC も同様で、BC6H/BC7(BPTC) と同じように 128bit 単位で圧縮を
行います。その仕組はさらに複雑でより踏み込んだ内容でした。
・128bit 単位
・複雑なモード分岐
・Dual Plane 機能 (Index を 2セット持つ)
・Partition 分割 (カラーセット Endpoint を複数持つ)
これらの特徴は BC6H/BC7(BPTC) と共通のものです。
ASTC にはさらに独自の仕様が多数含まれています。
・可変 Block サイズ (4x4 ~ 12x12)
・最大 4分割 の Partition (BPTC は 3分割まで)
・2,3,5 の 3種類の基数選択
・1~4 component の自由なフォーマット (R,RG,RGB,RGBA)
Khronos の仕様では LDR モードのみ定義されていますが、
ARM のドキュメントでは 3D texture や HDR まで言及されており
より幅広い用途が想定されていたことがわかります。
・KHR_texture_compression_astc_ldr
・ARM Mali developer Center: ASTC Evaluation Codec
bit field は非常に自由で、モードと Endpoint (代表カラー値) が
下位 bit から、Index (Texel Weight) が上位 bit から必要個数並ぶという
可変フォーマットです。
そのため組み合わせ数は膨大で単純なモード数では表現できません。
また 2進数からくる制約が少ないのも大きな特徴の一つです。
例えば block サイズも 5x5 , 6x6 , 10x8 など中途半端なサイズを
選ぶことができます。
Index によるカラーの補間も、DXT 等では 2bit なので 4段階ですが、
ASTC では 3値や 5値等の任意の段階を選ぶことができます。
DXT では 2色の代表カラーを選んでも、4階調だとちょうど中間の値を
生成することができません。画像によっては都合が悪く、敢えて
DXT1 の 3色モード (Alpha 1bit) を用いることがありました。
ASTC は 3 と 5 の基数を用いることができます。
5つの 3階調値を 8bit (3^5 = 243) に格納し、同様に 3つの 5階調値を
7bit (5^3 = 125) に収めます。
これに任意ビット数のスケール値を掛けることで、2 の n 乗に制限されず
下記の値の範囲を格納することができます。
3 (0-2) = 3 + 0bit 4 (0-3) = 2bit 5 (0-4) = 5 + 0bit 6 (0-5) = 3 + 1bit = 3 * 2 8 (0-7) = 3bit 10 (0-9) = 5 + 1bit = 5 * 2 12 (0-11)= 3 + 2bit = 3 * 4 :
例えば 12 (0~11) 段階をエンコードするなら 5個単位で格納します。
それぞれが 2bit のスケールを持つので、8bit + 5*2bit = 18bit (5個分)
となります。
このようにより自由なエンコードアルゴリズムを用いつつ、かつハードウエアで
実装しやすいようさまざまな工夫が施されています。
関連エントリ
・OpenGL 4.3/ES 3.0 ETC2 Texture 圧縮の仕組み (PVRTC2,ASTC)
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
・OpenGL の圧縮テクスチャ (3) BPTC, BC6H/BC7
・OpenGL の圧縮テクスチャ (2) 法線圧縮
・Android OpenGL ES 2.0 の圧縮テクスチャ
DirectX10 で大きく仕様が変わったのは、より汎用化を進めるためでした。
つまり専用の機能が無くなったということ。
ライティングのための機能、マテリアルの設定などといった目的別の API は
完全に姿を消しています。
では CPU のように完全に何にでも使える、単純でフラットな空間が得られたのかと言えば必ずしも
そうではなく、特にリソースとバッファの扱いはかえって複雑になった印象を受けます。
汎用化や何にでも使えることが、そのまま単純化に繋がるとは限らないためです。
複数の機能の要素をマージしたこと、目的別の機能の代わりにより抽象的な用語や仕様が
増えたことがその要因といえるかもしれません。
DirectX11 はさらに CompteShader が使えるようになり、書き込み可能なバッファが
追加されています。
以下 FEATURE_LEVEL 11.0、 ShaderModel 5.0 を対象としています。
●リソース
Direct3D 11 では View も ShaderObject も種類が増えており何を見て良いのか
わからなくなります。
でもリソースを作る手段は二つだけ。CreateBuffer() か CreateTexture~() です。
Constant も Buffer だし、Vertex や Index もただの Buffer です。
● View
リソースをどう扱うか、それを決めるのが View です。
同じリソースを複数の View を通して見ることが可能で、テクスチャとして参照したり
レンダリングしたり、データとして読み書きするなど流用が可能となります。
View は次の 4種類だけです。
・読み込みのための View、テクスチャやバッファなど
ShaderResourceView (SRV)
・Merger による書き込みのための View
RenderTargetView (RTV)
DepthStencilView (DSV)
・読み書きのための View
UnorderedAccessView (UAV)
VertexBuffer/IndexBuffer/ConstantBuffer は View を用いずに設定します。
●タイプとシェーダーオブジェクト
データをアクセスする方法を決定するのが、バッファ作成時の細かい指定と
DXGI フォーマットタイプです。
これらのシェーダーオブジェクトは、シェーダーがアクセスする方法を決定しています。
シェーダー側の宣言だけではだめで、リソース生成時にもいろいろとフラグを指定
しておく必要があります。
● UnorderedAccessView
RW がついているものと AppendSturctutedBuffer/ConsumeStructuredBuffer は
書き込み可能なシェーダーオブジェクトです。
これらは UnorderedAccessView (UAV) を使います。
UAV が使えるのは PixelShader と CompteShader だけ。
Compte Shader は Output Merger が無いので、出力のために必ず何らかの UAV を
用いることになります。
実際に使ってみると、出力はすべて UnorderedAccessView に相当することがわかってきます。
つまり RenderTarget も出力先アドレスが固定された UnorderedAccessView 相当だということです。
UAV の設定 API は次の通り
OMSetRenderTargetsAndUnorderedAccessViews() で一見何のために存在する
パラメータなのかわかりにくいのが UAVStartSlot です。
これは RenderTarget との衝突を避けるためのものでした。
例えば RenderTarget を 2 枚設定した場合は、UnorderedAccessView は必ず
番号 2 から始めなければなりません。
下記のようなリソースを登録する場合
RenderTarget x2
UnorderedAccessView x3
OMSetRenderTargetsAndUnorderedAccessViews() には次のようなパラメータを
渡します。
※ August 2009 のマニュアル OMSetRenderTargetsAndUnorderedAccessViews()
の引数は若干間違いがあります。ヘッダファイルを見た方が正しい宣言です。
シェーダーレジスタはこのように割り当てられます。
つまり o レジスタと u レジスタは同時に同じ番号を使うことが出来ず、
使用可能な View はあわせて 8 個まで。この両者ほぼ同じ扱いです。
UnorderedAccessView は読み書き可能なバッファです。
よく考えると RenderTarget や DepthBuffer と同じこと。
UAV は読み込んだあとにブレンドしたり、判定した結果をまた書き戻すことが出来るわけです。
そのパス内で更新できる OM をプログラムできるなら、いろいろ面白いことが出来そうです。
実際試してみたらエラーで、RW で読み込めるのは sngle コンポーネントに限ると言われます。
時間がなかったので、このあたりは後ほどまた詳しく調べてみます。
昔わからないと書いた CSSetUnorderedAccessViews() の最後のパラメータは
Append/Consume buffer に関するものでした。
やはり当時のサンプルの使い方が間違っていたのだと思われます。
関連エントリ
・DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・その他 Direct3D 11 関連
つまり専用の機能が無くなったということ。
ライティングのための機能、マテリアルの設定などといった目的別の API は
完全に姿を消しています。
では CPU のように完全に何にでも使える、単純でフラットな空間が得られたのかと言えば必ずしも
そうではなく、特にリソースとバッファの扱いはかえって複雑になった印象を受けます。
汎用化や何にでも使えることが、そのまま単純化に繋がるとは限らないためです。
複数の機能の要素をマージしたこと、目的別の機能の代わりにより抽象的な用語や仕様が
増えたことがその要因といえるかもしれません。
DirectX11 はさらに CompteShader が使えるようになり、書き込み可能なバッファが
追加されています。
以下 FEATURE_LEVEL 11.0、 ShaderModel 5.0 を対象としています。
●リソース
Direct3D 11 では View も ShaderObject も種類が増えており何を見て良いのか
わからなくなります。
でもリソースを作る手段は二つだけ。CreateBuffer() か CreateTexture~() です。
Constant も Buffer だし、Vertex や Index もただの Buffer です。
● View
リソースをどう扱うか、それを決めるのが View です。
同じリソースを複数の View を通して見ることが可能で、テクスチャとして参照したり
レンダリングしたり、データとして読み書きするなど流用が可能となります。
View は次の 4種類だけです。
・読み込みのための View、テクスチャやバッファなど
ShaderResourceView (SRV)
・Merger による書き込みのための View
RenderTargetView (RTV)
DepthStencilView (DSV)
・読み書きのための View
UnorderedAccessView (UAV)
VertexBuffer/IndexBuffer/ConstantBuffer は View を用いずに設定します。
●タイプとシェーダーオブジェクト
データをアクセスする方法を決定するのが、バッファ作成時の細かい指定と
DXGI フォーマットタイプです。
Texture Buffer ByteAdddressBuffer StructuredBuffer AppendSturctutedBuffer ConsumeStructuredBuffer RWTexture RWBuffer RWSturcturedBuffer RWByteAddressBuffer
これらのシェーダーオブジェクトは、シェーダーがアクセスする方法を決定しています。
シェーダー側の宣言だけではだめで、リソース生成時にもいろいろとフラグを指定
しておく必要があります。
● UnorderedAccessView
RW がついているものと AppendSturctutedBuffer/ConsumeStructuredBuffer は
書き込み可能なシェーダーオブジェクトです。
これらは UnorderedAccessView (UAV) を使います。
UAV が使えるのは PixelShader と CompteShader だけ。
Compte Shader は Output Merger が無いので、出力のために必ず何らかの UAV を
用いることになります。
実際に使ってみると、出力はすべて UnorderedAccessView に相当することがわかってきます。
つまり RenderTarget も出力先アドレスが固定された UnorderedAccessView 相当だということです。
UAV の設定 API は次の通り
CompteShader → CSSetUnorderedAccessViews() PixelShader → OMSetRenderTargetsAndUnorderedAccessViews()
OMSetRenderTargetsAndUnorderedAccessViews() で一見何のために存在する
パラメータなのかわかりにくいのが UAVStartSlot です。
これは RenderTarget との衝突を避けるためのものでした。
例えば RenderTarget を 2 枚設定した場合は、UnorderedAccessView は必ず
番号 2 から始めなければなりません。
下記のようなリソースを登録する場合
RenderTarget x2
UnorderedAccessView x3
OMSetRenderTargetsAndUnorderedAccessViews() には次のようなパラメータを
渡します。
NumViews = 2 // RTV (RenderTargetView) の個数 UAVStartSlot = 2 // UAV (UnorderedAccessView) の開始番号 == NumViews NumUAVs = 3 // UAV の個数
※ August 2009 のマニュアル OMSetRenderTargetsAndUnorderedAccessViews()
の引数は若干間違いがあります。ヘッダファイルを見た方が正しい宣言です。
シェーダーレジスタはこのように割り当てられます。
o0 = RenderTarget o1 = RenderTarget u2 = UnorderedAccessView u3 = UnorderedAccessView u4 = UnorderedAccessView
つまり o レジスタと u レジスタは同時に同じ番号を使うことが出来ず、
使用可能な View はあわせて 8 個まで。この両者ほぼ同じ扱いです。
UnorderedAccessView は読み書き可能なバッファです。
よく考えると RenderTarget や DepthBuffer と同じこと。
UAV は読み込んだあとにブレンドしたり、判定した結果をまた書き戻すことが出来るわけです。
そのパス内で更新できる OM をプログラムできるなら、いろいろ面白いことが出来そうです。
実際試してみたらエラーで、RW で読み込めるのは sngle コンポーネントに限ると言われます。
時間がなかったので、このあたりは後ほどまた詳しく調べてみます。
昔わからないと書いた CSSetUnorderedAccessViews() の最後のパラメータは
Append/Consume buffer に関するものでした。
やはり当時のサンプルの使い方が間違っていたのだと思われます。
関連エントリ
・DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・その他 Direct3D 11 関連
2009/08/10
Direct3D11 Windows7 RTM と DebugLayer
Windows7 を RTM 版に入れ替えたら D3D11(beta) のプログラムが動かなくなって
いました。DirectX SDK March2009 + D3D11 を使用しており、RC まではきちんと
動いていたものです。
止まっているのが CommandList まわりの呼び出しで関係ない関数に飛んでいます。
lpVtbl がずれているような感じ。
単に beta と互換性がなくなっているだけかもしれません。
新しい Windows7 SDK には D3D 関連のヘッダや lib も含まれているため、
こちらを使えば RTM 版 dll が用いられます。
d3dx11 や d3dcompiler は無いので、この辺を使っている場合は DirectX SDK も
併用することになります。core ではないためおそらく大丈夫だと思います。
でも結局 Windows7 SDK の d3d11.lib に切り替えてもだめ。
コンソールをみると d3d11.dll だけでなく最後に d3d11_beta.dll も読み込まれて
いました。この両者が混在しているのはあまりよい感じではありません。
DirectX SDK サンプルは動くしサンプルをビルドしても大丈夫です。
サンプルとの違いは DXGI1.1 を使っていることくらいでしょうか。
試しに DXGI1 に戻してもやっぱり動きません。
DXGI1.1 は Windows SDK 側の dxgi.lib で使用できるはずなので、問題ないと
思います。
lib の検索順番や、リンクする lib の組み合わせをいろいろテスト。
気になるのは Windows SDK 側の d3d11.lib のみ使うようにしても、必ず最後に
d3d11_beta.dll が読み込まれてしまうことです。
誰が読み込んでいるのだろうと調べているうちに DebugLayer が原因ではないかと
思いつきました。
D3D11Create~() で D3D11_CREATE_DEVICE_DEBUG を指定していると、
Debug でも Release build でも最後に d3d11_beta.dll が読み込まれています。
とりあえずこのフラグ指定を消すと Windows7 RTM で動作しました。
やはり d3d11_beta.dll との互換性が問題だと思われます。
DirectX SDK がリリースされるまで待った方が良さそうです。
いました。DirectX SDK March2009 + D3D11 を使用しており、RC まではきちんと
動いていたものです。
止まっているのが CommandList まわりの呼び出しで関係ない関数に飛んでいます。
lpVtbl がずれているような感じ。
単に beta と互換性がなくなっているだけかもしれません。
新しい Windows7 SDK には D3D 関連のヘッダや lib も含まれているため、
こちらを使えば RTM 版 dll が用いられます。
d3dx11 や d3dcompiler は無いので、この辺を使っている場合は DirectX SDK も
併用することになります。core ではないためおそらく大丈夫だと思います。
でも結局 Windows7 SDK の d3d11.lib に切り替えてもだめ。
コンソールをみると d3d11.dll だけでなく最後に d3d11_beta.dll も読み込まれて
いました。この両者が混在しているのはあまりよい感じではありません。
DirectX SDK サンプルは動くしサンプルをビルドしても大丈夫です。
サンプルとの違いは DXGI1.1 を使っていることくらいでしょうか。
試しに DXGI1 に戻してもやっぱり動きません。
DXGI1.1 は Windows SDK 側の dxgi.lib で使用できるはずなので、問題ないと
思います。
lib の検索順番や、リンクする lib の組み合わせをいろいろテスト。
気になるのは Windows SDK 側の d3d11.lib のみ使うようにしても、必ず最後に
d3d11_beta.dll が読み込まれてしまうことです。
誰が読み込んでいるのだろうと調べているうちに DebugLayer が原因ではないかと
思いつきました。
D3D11Create~() で D3D11_CREATE_DEVICE_DEBUG を指定していると、
Debug でも Release build でも最後に d3d11_beta.dll が読み込まれています。
とりあえずこのフラグ指定を消すと Windows7 RTM で動作しました。
やはり d3d11_beta.dll との互換性が問題だと思われます。
DirectX SDK がリリースされるまで待った方が良さそうです。
2015/04/24
Direct3D 12 と ASTC 圧縮 Texture
オプション扱いですが Direct3D12/Direct3D11.3 では新たに
ASTC 圧縮テクスチャがサポートされています。
ASTC は OpenGL ES 3.0 で導入された新しい圧縮形式です。
DirectX では BC 系以外では初であり、モバイルからの逆輸入になります。
・MSDN DXGI_FORMAT
これで dds ファイルに ASTC 画像を格納できるようになりました。
下記ページの一覧表に DXGI_FORMAT_ASTC_* を追加しています。
・圧縮 Texture
ASTC について以前の解説
・OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC
● 64bit 単位の圧縮
圧縮テクスチャの多くは 4x4 block 単位で 64bit に圧縮します。
例えば下記のフォーマットは 64bit です。
64 bit/16 pixel なので 1pixel あたり 4bit (4bpp)。
DXT3/5 (BC2/3) や ETC2-EAC などアルファ付きフォーマットは 128bit 8bpp ですが、
内部的には 64bit/block の構造が 2個並んでいるだけです。
処理の単位はあくまで 64bit になります。
● 128bit 単位の圧縮フォーマット
64bit/block を第一世代とすれば、BC6H/BC7 は 128bit/block の第二世代の
圧縮テクスチャに相当します。
64bit では 16個の index 値がエンコードスペース大半を占めるので
あまり複雑なアルゴリズムを選ぶことができませんでした。
追加の圧縮情報が増えるとベースカラー (Endpoint) の bit を減らすしかないので
カラー精度と圧縮アルゴリズムのどちらを取るかトレードオフになります。
128bit/block ならスペースに余裕があるためフォーマットの自由度が増します。
BC6H/BC7 はこの追加容量を、HDR 対応化や高画質化に割り当てています。
8~14 種類のアルゴリズム選択、Partition 分割による複数の Endpoint セットなど
より複雑なエンコードが行われています。
画質が向上する反面 BC6H/BC7 は容量が増えます。
単純な RGB カラーでは DXT1 の 2倍なので、圧縮率を優先する場合は BC7 は
あまり出番が無いかもしれません。
● 圧縮率とブロックサイズ
block あたりの容量を減らさなくても、block サイズを増やせば圧縮率が上がります。
その代表例が PVRTC/PVRTC2 の 2bpp フォーマットです。
PVRTC は 64bit/block ですが、block サイズを 8x4 に広げることで 2bpp まで
圧縮することができます。
ただしこの場合 index 数が増えるためピクセルの階調が減ります。
index は 8x4 = 32個必要なので 1pixel あたり 1bit になるためです。
● ASTC
ASTC は 128bit/block の第二世代に属し、128bit エリアをフルに使った
複雑なエンコードを行います。
しかしながら block サイズを可変にすることで、同時に高い圧縮率も実現しています。
4x4 block の場合は BC6H/BC7 同様 8bpp ですが、
6x6 block ではおよそ 3.6bpp、
8x8 block では 2bpp と
目的に合わせて圧縮率を選ぶことができます。
もちろん圧縮率を上げると画質はそれなりに下がります。
・OpenGL ES 3.0 / OpenGL 4.3 ASTC 圧縮テクスチャの比較
ASTC の注意点は、これまで絶対安全だった 256x256 や 512x512 といった
2^n サイズのテクスチャが必ずしも割り切れないことです。
例えば ASTC 6x6 の場合、 256x256 サイズのテクスチャも内部的には
258x258 でデータを持つことになります。
テクスチャローダーでは注意が必要です。
bpp が割り切れる整数値にならないことと、テクスチャサイズで端数が生じることから
ASTC 対応のためにいくつかの手直しが必要になるかもしれません。
関連ページ
・HYPERでんち: 圧縮 Texture 一覧
・HYPERでんち: 解説記事一覧
関連エントリ
・iPad Air 2 (Apple A8X) の GPU
・OpenGL ES 3.0 / OpenGL 4.3 ASTC 圧縮テクスチャの比較
・OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
ASTC 圧縮テクスチャがサポートされています。
ASTC は OpenGL ES 3.0 で導入された新しい圧縮形式です。
DirectX では BC 系以外では初であり、モバイルからの逆輸入になります。
・MSDN DXGI_FORMAT
これで dds ファイルに ASTC 画像を格納できるようになりました。
下記ページの一覧表に DXGI_FORMAT_ASTC_* を追加しています。
・圧縮 Texture
ASTC について以前の解説
・OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC
● 64bit 単位の圧縮
圧縮テクスチャの多くは 4x4 block 単位で 64bit に圧縮します。
例えば下記のフォーマットは 64bit です。
DXT1(BC1), ETC1, ETC2(RGB), ATITC(RGB), PVRTC 4bpp, PVRTC2 4bpp
64 bit/16 pixel なので 1pixel あたり 4bit (4bpp)。
DXT3/5 (BC2/3) や ETC2-EAC などアルファ付きフォーマットは 128bit 8bpp ですが、
内部的には 64bit/block の構造が 2個並んでいるだけです。
処理の単位はあくまで 64bit になります。
DXT1 (BC1) +-----------+ |COLOR 64bit| +-----------+ DXT3 (BC2) DXT1 + A4 +-----------+ +-----------+ |COLOR 64bit| |ALPHA 64bit| +-----------+ +-----------+ DXT5 (BC3) DXT1 + BC4 +-----------+ +-----------+ |COLOR 64bit| |ALPHA 64bit| +-----------+ +-----------+ ETC2 +-----------+ |COLOR 64bit| +-----------+ EAC (EAC-R11) +-----------+ |RED 64bit| +-----------+ ETC2-EAC ETC2 + EAC +-----------+ +-----------+ |COLOR 64bit| |ALPHA 64bit| +-----------+ +-----------+ EAC_RG11 EAC + EAC +-----------+ +-----------+ |RED 64bit| |GREEN 64bit| +-----------+ +-----------+
● 128bit 単位の圧縮フォーマット
64bit/block を第一世代とすれば、BC6H/BC7 は 128bit/block の第二世代の
圧縮テクスチャに相当します。
64bit では 16個の index 値がエンコードスペース大半を占めるので
あまり複雑なアルゴリズムを選ぶことができませんでした。
追加の圧縮情報が増えるとベースカラー (Endpoint) の bit を減らすしかないので
カラー精度と圧縮アルゴリズムのどちらを取るかトレードオフになります。
128bit/block ならスペースに余裕があるためフォーマットの自由度が増します。
BC6H/BC7 はこの追加容量を、HDR 対応化や高画質化に割り当てています。
8~14 種類のアルゴリズム選択、Partition 分割による複数の Endpoint セットなど
より複雑なエンコードが行われています。
画質が向上する反面 BC6H/BC7 は容量が増えます。
単純な RGB カラーでは DXT1 の 2倍なので、圧縮率を優先する場合は BC7 は
あまり出番が無いかもしれません。
● 圧縮率とブロックサイズ
block あたりの容量を減らさなくても、block サイズを増やせば圧縮率が上がります。
その代表例が PVRTC/PVRTC2 の 2bpp フォーマットです。
PVRTC は 64bit/block ですが、block サイズを 8x4 に広げることで 2bpp まで
圧縮することができます。
ただしこの場合 index 数が増えるためピクセルの階調が減ります。
index は 8x4 = 32個必要なので 1pixel あたり 1bit になるためです。
● ASTC
ASTC は 128bit/block の第二世代に属し、128bit エリアをフルに使った
複雑なエンコードを行います。
しかしながら block サイズを可変にすることで、同時に高い圧縮率も実現しています。
4x4 block の場合は BC6H/BC7 同様 8bpp ですが、
6x6 block ではおよそ 3.6bpp、
8x8 block では 2bpp と
目的に合わせて圧縮率を選ぶことができます。
もちろん圧縮率を上げると画質はそれなりに下がります。
・OpenGL ES 3.0 / OpenGL 4.3 ASTC 圧縮テクスチャの比較
ASTC の注意点は、これまで絶対安全だった 256x256 や 512x512 といった
2^n サイズのテクスチャが必ずしも割り切れないことです。
例えば ASTC 6x6 の場合、 256x256 サイズのテクスチャも内部的には
258x258 でデータを持つことになります。
テクスチャローダーでは注意が必要です。
bpp が割り切れる整数値にならないことと、テクスチャサイズで端数が生じることから
ASTC 対応のためにいくつかの手直しが必要になるかもしれません。
関連ページ
・HYPERでんち: 圧縮 Texture 一覧
・HYPERでんち: 解説記事一覧
関連エントリ
・iPad Air 2 (Apple A8X) の GPU
・OpenGL ES 3.0 / OpenGL 4.3 ASTC 圧縮テクスチャの比較
・OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (2)
2009/08/20
OpenGLES2.0 Direct3D とのフォーマット変換
Direct3D10 以降、Pixel フォーマットと頂点の各 Element のフォーマット形式は
統一されています。DXGI_FORMAT はまた Direct3D からも独立しており、
バージョンに依存せず同じものが使えるようになりました。
つまり Direct3D10 と Direct3D11 のフォーマットは同一です。
Direct3D11 ではライブラリ独自の頂点形式から ID3D11InputLayout を作るために
下記の関数を用意していました。
これをそのまま OpenGLES.20 用に置き換えたのが、先日の GLInputLayout の関数です。
このように、独自の頂点形式をテーブルで DXGI_FORMAT に変換しています。
DXGI そのままでないのが幸いして、OpenGLES2.0 への変換も比較的うまく
いっています。OpenGLES2.0 で用意したのが下記のコードです。
このテーブルはそのまま上の DXGI_FORMAT に対応しています。
前回 GLInputLayout の CreateInputLayout() の中で呼び出していた
PixelToFormat() がこれです。
まだ FLOAT しか使っていないので本当に互換性があるかどうかは検証してません。
BYTE の並びは D3D9 の D3DDECLTYPE_UBYTE4 と D3DDECLTYPE_D3DCOLOR
のように順番が異なっている可能性があります。
同じく前回「ELEMENTFLAG_NORMALIZE の定義値が 1 なのは念のため。」と書いた
のはコンパイラの最適化で三項演算子が完全に消えることを期待していたからです。
GL_TRUE / GL_FALSE の定義は 1 と 0 なので、最終的には
「ep-
予想通り分岐は消えますが、シンボルが 1 以外でも shr が一つ追加されるだけでした。
シフトを命令に埋め込める ARM だったら全く意識する必要ないのかもしれません。
関連エントリ
・OpenGLES 2.0 頂点フォーマットの管理
・OpenGLES2.0 の頂点
・OpenGLES2.0 D3D座標系
・OpenGLES2.0 シェーダー管理
統一されています。DXGI_FORMAT はまた Direct3D からも独立しており、
バージョンに依存せず同じものが使えるようになりました。
つまり Direct3D10 と Direct3D11 のフォーマットは同一です。
●Direct3D9
・ピクセル、インデックス用フォーマット D3DFORMAT
D3DFMT_A8R8G8B8
D3DFMT_R5G6B5
D3DFMT_A16B16G16R16F
D3DFMT_INDEX16 (IndexBuffer 用)
等
・頂点フォーマット D3DDECLTYPE
D3DDECLTYPE_FLOAT3
D3DDECLTYPE_UBYTE4
他
●Direct3D10/Direct3D11
・ピクセル、頂点、インデックス兼用 DXGI_FORMAT
DXGI_FORMAT_R8G8B8A8_UNORM
DXGI_FORMAT_B5G6R5_UNORM
DXGI_FORMAT_R32G32B32_FLOAT (従来の D3DDECLTYPE_FLOAT3 も兼ねる)
DXGI_FORMAT_R16_UINT (従来の D3DFMT_INDEX16 も兼ねる)
~
Direct3D11 ではライブラリ独自の頂点形式から ID3D11InputLayout を作るために
下記の関数を用意していました。
これをそのまま OpenGLES.20 用に置き換えたのが、先日の GLInputLayout の関数です。
// DXInputLayout.cpp static struct _FormatType { DXGI_FORMAT dxgi_format; } formatTable[]= { { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R32_FLOAT, }, { DXGI_FORMAT_R32G32_FLOAT, }, { DXGI_FORMAT_R32G32B32_FLOAT, }, { DXGI_FORMAT_R32G32B32A32_FLOAT, }, { DXGI_FORMAT_R16_FLOAT, }, { DXGI_FORMAT_R16G16_FLOAT, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R16G16B16A16_FLOAT, }, { DXGI_FORMAT_R32_SINT, }, { DXGI_FORMAT_R32G32_SINT, }, { DXGI_FORMAT_R32G32B32_SINT, }, { DXGI_FORMAT_R32G32B32A32_SINT, }, { DXGI_FORMAT_R16_SINT, }, { DXGI_FORMAT_R16G16_SINT, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R16G16B16A16_SINT, }, { DXGI_FORMAT_R8_SINT, }, { DXGI_FORMAT_R8G8_SINT, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R8G8B8A8_SINT, }, { DXGI_FORMAT_R32_UINT, }, { DXGI_FORMAT_R32G32_UINT, }, { DXGI_FORMAT_R32G32B32_UINT, }, { DXGI_FORMAT_R32G32B32A32_UINT, }, { DXGI_FORMAT_R16_UINT, }, { DXGI_FORMAT_R16G16_UINT, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R16G16B16A16_UINT, }, { DXGI_FORMAT_R8_UINT, }, { DXGI_FORMAT_R8G8_UINT, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R8G8B8A8_UINT, }, { DXGI_FORMAT_R16_SNORM, }, { DXGI_FORMAT_R16G16_SNORM, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R16G16B16A16_SNORM, }, { DXGI_FORMAT_R8_SNORM, }, { DXGI_FORMAT_R8G8_SNORM, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R8G8B8A8_SNORM, }, { DXGI_FORMAT_R16_UNORM, }, { DXGI_FORMAT_R16G16_UNORM, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R16G16B16A16_UNORM, }, { DXGI_FORMAT_R8_UNORM, }, { DXGI_FORMAT_R8G8_UNORM, }, { DXGI_FORMAT_UNKNOWN, }, { DXGI_FORMAT_R8G8B8A8_UNORM, }, }; DXGI_FORMAT _flFASTCALL PixelToFormat( unsigned int func ) { assert( func < Image::FUNC_END ); return formatTable[ func ].dxgi_format; } void _flFASTCALL SetElementDesc( D3D11_INPUT_ELEMENT_DESC* desc, unsigned int desccount, const a5::InputLayout& layout ) { unsigned int ecount= layout.GetElementCount(); assert( ecount < desccount ); for( unsigned int ei= 0 ; ei< ecount ; ei++, desc++ ){ const a5::InputElement* ep= layout.GetElement( ei ); desc->SemanticName= ep->SemanticName; desc->SemanticIndex= ep->SemanticIndex; desc->Format= PixelToFormat( ep->pixf.Func ); desc->InputSlot= 0; desc->AlignedByteOffset= ep->ByteOffset; desc->InputSlotClass= D3D11_INPUT_PER_VERTEX_DATA; desc->InstanceDataStepRate= 0; } }
このように、独自の頂点形式をテーブルで DXGI_FORMAT に変換しています。
DXGI そのままでないのが幸いして、OpenGLES2.0 への変換も比較的うまく
いっています。OpenGLES2.0 で用意したのが下記のコードです。
このテーブルはそのまま上の DXGI_FORMAT に対応しています。
static struct _FormatType { GLenum dxgi_format; unsigned int component; unsigned int normalize; } formatTable[]= { { 0, 0, FALSE, }, { 0, 0, FALSE, }, { 0, 0, FALSE, }, { 0, 0, FALSE, }, { GL_FLOAT, 1, FALSE, }, { GL_FLOAT, 2, FALSE, }, { GL_FLOAT, 3, FALSE, }, { GL_FLOAT, 4, FALSE, }, { 0, 0, FALSE, }, // half float { 0, 0, FALSE, }, // half float { 0, 0, FALSE, }, // half float { 0, 0, FALSE, }, // half float { 0, 0, FALSE, }, // signed int 32 { 0, 0, FALSE, }, // signed int 32 { 0, 0, FALSE, }, // signed int 32 { 0, 0, FALSE, }, // signed int 32 { GL_SHORT, 1, FALSE, }, // signed int 16 { GL_SHORT, 2, FALSE, }, // signed int 16 { GL_SHORT, 3, FALSE, }, // signed int 16 { GL_SHORT, 4, FALSE, }, // signed int 16 { GL_BYTE, 1, FALSE, }, // signed byte 8 { GL_BYTE, 2, FALSE, }, // signed byte 8 { GL_BYTE, 3, FALSE, }, // signed byte 8 { GL_BYTE, 4, FALSE, }, // signed byte 8 { 0, 0, FALSE, }, // unsigned int 32 { 0, 0, FALSE, }, // unsigned int 32 { 0, 0, FALSE, }, // unsigned int 32 { 0, 0, FALSE, }, // unsigned int 32 { GL_UNSIGNED_SHORT, 1, FALSE, }, { GL_UNSIGNED_SHORT, 2, FALSE, }, { GL_UNSIGNED_SHORT, 3, FALSE, }, { GL_UNSIGNED_SHORT, 4, FALSE, }, { GL_UNSIGNED_BYTE, 1, FALSE, }, { GL_UNSIGNED_BYTE, 2, FALSE, }, { GL_UNSIGNED_BYTE, 3, FALSE, }, { GL_UNSIGNED_BYTE, 4, FALSE, }, { GL_SHORT, 1, TRUE, }, { GL_SHORT, 2, TRUE, }, { GL_SHORT, 3, TRUE, }, { GL_SHORT, 4, TRUE, }, { GL_BYTE, 1, TRUE, }, { GL_BYTE, 2, TRUE, }, { GL_BYTE, 3, TRUE, }, { GL_BYTE, 4, TRUE, }, { GL_UNSIGNED_SHORT, 1, TRUE, }, { GL_UNSIGNED_SHORT, 2, TRUE, }, { GL_UNSIGNED_SHORT, 3, TRUE, }, { GL_UNSIGNED_SHORT, 4, TRUE, }, { GL_UNSIGNED_BYTE, 1, TRUE, }, { GL_UNSIGNED_BYTE, 2, TRUE, }, { GL_UNSIGNED_BYTE, 3, TRUE, }, { GL_UNSIGNED_BYTE, 4, TRUE, }, }; GLenum _flFASTCALL PixelToFormat( unsigned int func, unsigned int* component, unsigned int* normal ) { assert( func < Image::FUNC_END ); const _FormatType* fp= &formatTable[ func ]; if( component ){ *component= fp->component; } if( normal ){ *normal= fp->normalize; } return fp->dxgi_format; }
前回 GLInputLayout の CreateInputLayout() の中で呼び出していた
PixelToFormat() がこれです。
まだ FLOAT しか使っていないので本当に互換性があるかどうかは検証してません。
BYTE の並びは D3D9 の D3DDECLTYPE_UBYTE4 と D3DDECLTYPE_D3DCOLOR
のように順番が異なっている可能性があります。
同じく前回「ELEMENTFLAG_NORMALIZE の定義値が 1 なのは念のため。」と書いた
のはコンパイラの最適化で三項演算子が完全に消えることを期待していたからです。
(ep-
GL_TRUE / GL_FALSE の定義は 1 と 0 なので、最終的には
「ep-
予想通り分岐は消えますが、シンボルが 1 以外でも shr が一つ追加されるだけでした。
; ELEMENTFLAG_NORMALIZE == 4 の場合 (x64 + VC) 000000013F4D6697 shr ebx,2 000000013F4D66A1 and ebx,1 000000013F4D66A4 mov edx,ebx
シフトを命令に埋め込める ARM だったら全く意識する必要ないのかもしれません。
関連エントリ
・OpenGLES 2.0 頂点フォーマットの管理
・OpenGLES2.0 の頂点
・OpenGLES2.0 D3D座標系
・OpenGLES2.0 シェーダー管理
2010/03/13
OpenGL 4.0 / OpenGL 3.3
新しい OpenGL 仕様が発表されました。
・OpenGL Overview
OpenGL 4.0 は一言で言えば Direct3D 11 (DirectX11)。
シェーダーステージも増えテセレータが追加されています。
OpenGL 3.2 がほぼ Direct3D 10 相当でしたが、これで Direct3D との機能差が
一気に無くなりました。
整数演算や倍精度浮動小数演算(double)も D3D11 同様に追加されています。
OpenGL 3.3 は OpenGL 3.2 の拡張版です。まだ機能差を詳しく確認していませんが
Direct3D 10 に対する 10.1 の位置づけでしょうか。
OpenGL 4.0/3.3 共に OpenGL のバージョンとシェーダー言語のバージョン番号が
統一されました。
使う側から見れば D3D11 のスレッド対応や豊富な圧縮テクスチャが使えることは
非常に大きな優位点です。
しかしながら OpenGL 4.0 でもテセレーションなどほぼ同等のことができるように
なっています。モバイル向け OpenGL ES と互換性を取りやすい点を考えると OpenGL は
かなり魅力的な環境になったといえるでしょう。
関連エントリ
・OpenGL GLSL のバージョン
・OpenGL Overview
OpenGL 4.0 は一言で言えば Direct3D 11 (DirectX11)。
シェーダーステージも増えテセレータが追加されています。
OpenGL 3.2 がほぼ Direct3D 10 相当でしたが、これで Direct3D との機能差が
一気に無くなりました。
Direct3D11/DirectX11 OpenGL 4.0
-----------------------------------------------------------------------------
Vertex Shader Vertex Shader (GL_VERTEX_SHADER)
Hull Shader Tessellation Control Shader (GL_TESS_CONTROL_SHADER)
Domain Shader Tessellation Evaluation Shader (GL_TESS_EVALUATION_SHADER)
Geometry Shader Geometry Shader (GL_GEOMETRY_SHADER)
Pixel Shader Fragment Shader (GL_FRAGMENT_SHADER)
Compute Shader OpenCL
整数演算や倍精度浮動小数演算(double)も D3D11 同様に追加されています。
OpenGL 3.3 は OpenGL 3.2 の拡張版です。まだ機能差を詳しく確認していませんが
Direct3D 10 に対する 10.1 の位置づけでしょうか。
OpenGL 4.0/3.3 共に OpenGL のバージョンとシェーダー言語のバージョン番号が
統一されました。
OpenGL 2.0 GLSL 1.1 vsh, fsh OpenGL 2.1 GLSL 1.2 vsh, fsh OpenGL 3.0 GLSL 1.3 vsh, fsh OpenGL 3.1 GLSL 1.4 vsh, fsh OpenGL 3.2 GLSL 1.5 vsh, fsh, gsh OpenGL 3.3 GLSL 3.3 vsh, fsh, gsh OpenGL 4.0 GLSL 4.0 vsh, fsh, gsh, tcsh, tesh tcsh= Tessellation Control Shader tesh= Tessellation Evaluation Shader
使う側から見れば D3D11 のスレッド対応や豊富な圧縮テクスチャが使えることは
非常に大きな優位点です。
しかしながら OpenGL 4.0 でもテセレーションなどほぼ同等のことができるように
なっています。モバイル向け OpenGL ES と互換性を取りやすい点を考えると OpenGL は
かなり魅力的な環境になったといえるでしょう。
関連エントリ
・OpenGL GLSL のバージョン
Windows7 RC と同時に WindowsSDK の RC 版も出ています。
・Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: RC
・Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: RC (ISO)
BETA 時点からタッチ周りの API など変更されているところがあるので要注意です。
たとえば WM_TOUCHDOWN, WM_TOUCHUP, WM_TOUCHMOVE 等は無くなり
WM_TOUCH に統一されているようです。
マニュアルも更新されています。
以前 User Interface の下にあった Touch 関連が Windows Touch
という新項目にまとめられています。このあたり力が入ってます。
・MSDN Windows Touch
Direct3D11 関連も DirectX SDK March 2009 より新しいものが含まれていました。
ただし d3d11.lib などの lib 名に _beta がついていないので include の順番に
注意した方が良さそうです。
Vista で使う場合は DXSDK の方を。
関連エントリ
・Windows7 Multitouch API その(2) WM_GESTURE 系
・Windows7 Multitouch API
・DirectX SDK March 2009
・Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: RC
・Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: RC (ISO)
BETA 時点からタッチ周りの API など変更されているところがあるので要注意です。
たとえば WM_TOUCHDOWN, WM_TOUCHUP, WM_TOUCHMOVE 等は無くなり
WM_TOUCH に統一されているようです。
マニュアルも更新されています。
以前 User Interface の下にあった Touch 関連が Windows Touch
という新項目にまとめられています。このあたり力が入ってます。
・MSDN Windows Touch
Direct3D11 関連も DirectX SDK March 2009 より新しいものが含まれていました。
ただし d3d11.lib などの lib 名に _beta がついていないので include の順番に
注意した方が良さそうです。
Vista で使う場合は DXSDK の方を。
関連エントリ
・Windows7 Multitouch API その(2) WM_GESTURE 系
・Windows7 Multitouch API
・DirectX SDK March 2009
2009/09/11
DirectX SDK August 2009 の解説と Direct3D 11 RTM
DirectX SDK August 2009 が出ています。
ついに Direct3D 11 の RTM 対応となりました。
・DirectX SDK (August 2009)
D3D11 自体は Windows 7 や Windows SDK for Windows7 で RTM 版が出ています。
デバッグ向けライブラリや D3DX、ツールを含んだ SDK としてはこれが最初です。
これで安心して Windows7 RTM 環境へ移行できそうです。
●大きなトピック
予想通りベータ版で欠けていた機能が追加されました。
例えば
・Effect (fx)
・BC6/7 対応コンバータ
特に Effect はベータに入っていてもおかしくない大きなコンポーネントです。
もしかして SDK のリリースが遅れた原因もこのあたりでしょうか。
マニュアルの完成度は高く、大幅に更新されているようです。
ドキュメントも March SDK で足りなかった各項目が埋まっており、説明も増えて結構
力が入っています。かなり好印象です。
また今回新たに明らかになった点もいくつかあります。特に次の 2つ
● FeatureLevel 9_3 は ShaderModel 2.0 だった
最初マニュアルの表記ミスかと思いました。
CapsViewer でも確かに 2.0 と表示されるので間違いではないようです。
● CapsViewer が復活した!!
FeatureLevel 登場時から結局 caps と同じことではないかと心配しましたが、
今回のリリースではそれを正式に認めたようなもの。
本当に CapsViewer に組み込まれてしまいました。
DXCapsViewer.exe を起動すると DXGI 1.1 Devices の欄が追加されています。
それぞれの FeatureLevel 毎に対応機能やフォーマットなど、各ドライバの対応状況を
確認することが出来ます。

●D3DCSX が追加された
D3DX の ComputeShader 版です。prefix は D3DX のまま。
D3DX*.dll, D3DCompiler*.dll のように、番号付きの dll が今後追加されていく
ことになるようです。
●バージョンと互換性
SDK のメジャー番号は 9 のままです。
厳密には DirectX 9 SDK の中に Direct3D 10 や Direct3D 11 が含まれている
扱いになります。とはいえ、便宜上 DirectX 11 と表記することも多いです。
DirectX だけでなく Direct2D や DirectWrite も含まれています。
Vista でも動作します。
●ID3D11DeviceContext の更新
・追加
ID3D11DeviceContext::CopyStructureCount()
・削除
ID3D11DeviceContext::GetTextFilterSize()
ID3D11DeviceContext::SetTextFilterSize()
以前 DebugLayer が動かなかった原因はこの辺にありそうです。
● D3D11_PRIMITIVE の追加
enum D3D11_PRIMITIVE が追加されています。
似たようなシンボルとしてすでに D3D11_PRIMITIVE_TOPOLOGY が存在しています。
定義を見ると STRIP 形式が外されているようです。D3D11_PRIMITIVE は唯一
D3D11_SHADER_DESC の中で用いられています。考えられる用途は下記の通りです。
・D3D11_PRIMITIVE_TOPOLOGY ( strip あり )
IA や SO など strip 形式を含めたプリミティブの指定。
・D3D11_PRIMITIVE ( strip 無し )
GeometryShader / HullShader など、頂点ストリームやキャッシュを通過し、
Triangle (Primitive) 単位で入力が必要なシェーダーの指定で用いる。
● BC6H/BC7 texture
新しい圧縮テクスチャフォーマットです。繰り返しになりますが、従来 DXT と
呼ばれていたフォーマットは Direct3D 10 より BC に名称が変わっています。
付属のコマンドラインツール texconvex.exe を使うと実際に BC6H/BC7 で圧縮できる
ようになりました。DxTex.exe などそれ以外のツールは Direct3D 9 のテクスチャしか
扱えないので要注意です。
実際に試しましたが、圧縮は非常に低速です。
最初 1600x1200 のデータを渡してしまってかなり後悔しました。
結局中断して 640x480 でやり直したくらい。それでも結構待たされています。
Core i7 920 なのに。
リファレンスデバイスを指定したアプリを作れば一応読み込むことが出来ました。
ただデータの一部が化けており正しく出ていない可能性があります。
DXT10 形式の dds データ化けは以前もあったので、使い方のミスかツール側の
問題かもしれません。
DXT10 ヘッダを追加した dds テクスチャは D3D10 で登場したものの、対応したツール
はまだあまり出ていません。128byte + 20byte と中途半端なヘッダサイズになって
しまうため、一括ロードだと SSE 対応も難しいという欠点があります。
●残るは GPU (とドライバ)
とりあえず 9_3 の Shader Model 2.0 と、DXCapsViewer には驚きました。
Direct3D 8 の改革が Direct3D 9 で完成したように、
Direct3D 10 で目指したものの完成形が Direct3D 11 だといえるでしょう。
手持ちのコードは March 2009 から Direct3D 11 に移行しましたが、互換性も取れる
ようになったし、かなり使いやすい印象です。
あとは対応 GPU が発売されるのを待つばかり。
関連エントリ
・Direct3D11 Windows7 RTM と DebugLayer
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
・DirectX SDK March 2009
・Gamefest2008 と Direct3D 11
・Direct3D10 と DDS テクスチャフォーマット
ついに Direct3D 11 の RTM 対応となりました。
・DirectX SDK (August 2009)
D3D11 自体は Windows 7 や Windows SDK for Windows7 で RTM 版が出ています。
デバッグ向けライブラリや D3DX、ツールを含んだ SDK としてはこれが最初です。
これで安心して Windows7 RTM 環境へ移行できそうです。
●大きなトピック
予想通りベータ版で欠けていた機能が追加されました。
例えば
・Effect (fx)
・BC6/7 対応コンバータ
特に Effect はベータに入っていてもおかしくない大きなコンポーネントです。
もしかして SDK のリリースが遅れた原因もこのあたりでしょうか。
マニュアルの完成度は高く、大幅に更新されているようです。
ドキュメントも March SDK で足りなかった各項目が埋まっており、説明も増えて結構
力が入っています。かなり好印象です。
また今回新たに明らかになった点もいくつかあります。特に次の 2つ
● FeatureLevel 9_3 は ShaderModel 2.0 だった
最初マニュアルの表記ミスかと思いました。
CapsViewer でも確かに 2.0 と表示されるので間違いではないようです。
11_0 sm5.0 10_1 sm4.1 10_0 sm4.0 9_3 sm2.0 (4_0_level_9_3) << ここ 9_2 sm2.0 (4_0_level_9_1) 9_1 sm2.0 (4_0_level_9_1)
● CapsViewer が復活した!!
FeatureLevel 登場時から結局 caps と同じことではないかと心配しましたが、
今回のリリースではそれを正式に認めたようなもの。
本当に CapsViewer に組み込まれてしまいました。
DXCapsViewer.exe を起動すると DXGI 1.1 Devices の欄が追加されています。
それぞれの FeatureLevel 毎に対応機能やフォーマットなど、各ドライバの対応状況を
確認することが出来ます。

●D3DCSX が追加された
D3DX の ComputeShader 版です。prefix は D3DX のまま。
D3DX*.dll, D3DCompiler*.dll のように、番号付きの dll が今後追加されていく
ことになるようです。
●バージョンと互換性
SDK のメジャー番号は 9 のままです。
厳密には DirectX 9 SDK の中に Direct3D 10 や Direct3D 11 が含まれている
扱いになります。とはいえ、便宜上 DirectX 11 と表記することも多いです。
DirectX だけでなく Direct2D や DirectWrite も含まれています。
Vista でも動作します。
●ID3D11DeviceContext の更新
・追加
ID3D11DeviceContext::CopyStructureCount()
・削除
ID3D11DeviceContext::GetTextFilterSize()
ID3D11DeviceContext::SetTextFilterSize()
以前 DebugLayer が動かなかった原因はこの辺にありそうです。
● D3D11_PRIMITIVE の追加
enum D3D11_PRIMITIVE が追加されています。
似たようなシンボルとしてすでに D3D11_PRIMITIVE_TOPOLOGY が存在しています。
定義を見ると STRIP 形式が外されているようです。D3D11_PRIMITIVE は唯一
D3D11_SHADER_DESC の中で用いられています。考えられる用途は下記の通りです。
・D3D11_PRIMITIVE_TOPOLOGY ( strip あり )
IA や SO など strip 形式を含めたプリミティブの指定。
・D3D11_PRIMITIVE ( strip 無し )
GeometryShader / HullShader など、頂点ストリームやキャッシュを通過し、
Triangle (Primitive) 単位で入力が必要なシェーダーの指定で用いる。
● BC6H/BC7 texture
新しい圧縮テクスチャフォーマットです。繰り返しになりますが、従来 DXT と
呼ばれていたフォーマットは Direct3D 10 より BC に名称が変わっています。
BC1 4bpp ← DXT1 BC2 8bpp ← DXT2/3 BC3 8bpp ← DXT4/5 BC4 4bpp ← 1チャンネル圧縮 BC5 8bpp ← 2チャンネル圧縮, 法線圧縮 (3Dc/ATI2) BC6 8bpp ← (BC6H_UF16, BC6H_SF16) BC7 8bpp ← (BC7_UNORM, BC7_UNORM_SRGB)
付属のコマンドラインツール texconvex.exe を使うと実際に BC6H/BC7 で圧縮できる
ようになりました。DxTex.exe などそれ以外のツールは Direct3D 9 のテクスチャしか
扱えないので要注意です。
実際に試しましたが、圧縮は非常に低速です。
最初 1600x1200 のデータを渡してしまってかなり後悔しました。
結局中断して 640x480 でやり直したくらい。それでも結構待たされています。
Core i7 920 なのに。
リファレンスデバイスを指定したアプリを作れば一応読み込むことが出来ました。
ただデータの一部が化けており正しく出ていない可能性があります。
DXT10 形式の dds データ化けは以前もあったので、使い方のミスかツール側の
問題かもしれません。
DXT10 ヘッダを追加した dds テクスチャは D3D10 で登場したものの、対応したツール
はまだあまり出ていません。128byte + 20byte と中途半端なヘッダサイズになって
しまうため、一括ロードだと SSE 対応も難しいという欠点があります。
●残るは GPU (とドライバ)
とりあえず 9_3 の Shader Model 2.0 と、DXCapsViewer には驚きました。
Direct3D 8 の改革が Direct3D 9 で完成したように、
Direct3D 10 で目指したものの完成形が Direct3D 11 だといえるでしょう。
手持ちのコードは March 2009 から Direct3D 11 に移行しましたが、互換性も取れる
ようになったし、かなり使いやすい印象です。
あとは対応 GPU が発売されるのを待つばかり。
関連エントリ
・Direct3D11 Windows7 RTM と DebugLayer
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
・DirectX SDK March 2009
・Gamefest2008 と Direct3D 11
・Direct3D10 と DDS テクスチャフォーマット
ComputeShader はかなり便利なことがわかってきました。
ポストフィルタなどのピクセル処理だけでなく、VertexShader の前段に頂点演算として
挿入したり、頂点シェーダーの代わりに使ったりも出来ます。
標準描画パイプラインへのちょっとした機能追加が簡単にできるようになった印象です。
これまでは Stream Output や PixelShader を使っていた処理が ComputeShader
だけで済むわけです。
●ここが簡単
・読み込み位置、書き込み位置が固定されない
与えるリソースはどれも任意アドレスに対してアクセス可能で、特別扱いする
ものがありません。
・設定が項目が少ない
ConstantBuffer, ShaderResourceView, UnorderedAccessView のわずか 3つだけ。
サンプラーを使う場合は + 1。
・設定が独立している
描画用のステートをいじる必要がないので、パラメータを保存したり復帰させなくて
済みます。たとえば Viewport とか、RenderTarget とか、
DepthStencilState とか!! 描画に影響与えないし、戻さなくてもいいんです。
特に 3番目。いちいち DepthStencilState を作ったり、Depth を disable にしたり
しなくても良いだけで CS ありがとう、といった感じ。
● CS の制限
今までの Shader から見れば制限無しに扱いやすい ComputeShader ですが、
VertexShader 代わりに使おうとするといくつか制限も生じます。
・グループ内の実行スレッド数はシェーダー内に記述し、実行時に書き換えできない。
・Dispatch() に与えられる実行回数は x,y,z それぞれ 65535 まで。
・グループ内のスレッド数は 1024 まで。
よって VertexShader のように、1~100万回 など任意の実行回数が与えられた場合に
どのように Compute Shader を呼び出せばよいのか悩みます。
1. 65535 回を超える場合
x, y, z に分けるにしろ、CS 内のアトリビュートで Group 内スレッド数を増やすにしろ、
実行回数が常に定数で割り切れるとは限らない。
2. 速度
ある程度グループ内のスレッド数を大きめの値にしなければ ComputeShader の実行
速度が落ちます。65535 回未満だからと言って、Dispatch() だけで回数を指定して
下記のようなシェーダーを走らせると非常に低速になります。
●解決案
二通りの手段を考えてみました。
(1) 複数のシェーダーに分ける
numthreads = 32 などグループスレッド数を増やしたものと、numthreads = 1 の
端数を処理するスレッドに分けて実行します。
下のプログラムは 32 で割り切れる回数分 cmain_loop32 を実行し、端数を
cmain_loop1 で処理しています。
例えば 75 個のデータを処理するなら、cmain_loop32 を 2回、cmain_loop1 を 11回分
実行します。
(2) 動的分岐を用いる
端数込みで 32 の倍数分実行します。スレッド番号が実行したい回数より多ければ、
動的分岐で処理を省きます。
例えば 75 個のデータを処理するなら、cmain_loop_dis を 3 回 (96回分) 実行し、
id が 75 以上なら何もしないで終了します。
●実行結果
RADEON HD 5870 で試してみました。
1 フレームあたり 7セット Compute Shader の実行を繰り返しています。
それ以外の描画は fps などのフォントのみ。
上のプログラムはグループ内スレッド数 32 固定でしたが、16~320 まで変更して
試しています。
GroupThread が小さいと低速です。あまり小さいと Dispatch() の 65535 制限にも
ひっかかります。
どのケースでも、分岐を用いた (2) の方が高速でした。
端数分とはいえ GroupThread=1 で実行しているスレッドがあるため効率が悪い、
7セットの実行中に毎回シェーダー切り替えが発生しているから、切り替えないで済む
(2) の方が条件的に有利、等の理由が考えられます。
関連エントリ
・DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
ポストフィルタなどのピクセル処理だけでなく、VertexShader の前段に頂点演算として
挿入したり、頂点シェーダーの代わりに使ったりも出来ます。
標準描画パイプラインへのちょっとした機能追加が簡単にできるようになった印象です。
これまでは Stream Output や PixelShader を使っていた処理が ComputeShader
だけで済むわけです。
●ここが簡単
・読み込み位置、書き込み位置が固定されない
与えるリソースはどれも任意アドレスに対してアクセス可能で、特別扱いする
ものがありません。
・設定が項目が少ない
ConstantBuffer, ShaderResourceView, UnorderedAccessView のわずか 3つだけ。
サンプラーを使う場合は + 1。
・設定が独立している
描画用のステートをいじる必要がないので、パラメータを保存したり復帰させなくて
済みます。たとえば Viewport とか、RenderTarget とか、
DepthStencilState とか!! 描画に影響与えないし、戻さなくてもいいんです。
特に 3番目。いちいち DepthStencilState を作ったり、Depth を disable にしたり
しなくても良いだけで CS ありがとう、といった感じ。
● CS の制限
今までの Shader から見れば制限無しに扱いやすい ComputeShader ですが、
VertexShader 代わりに使おうとするといくつか制限も生じます。
・グループ内の実行スレッド数はシェーダー内に記述し、実行時に書き換えできない。
・Dispatch() に与えられる実行回数は x,y,z それぞれ 65535 まで。
・グループ内のスレッド数は 1024 まで。
よって VertexShader のように、1~100万回 など任意の実行回数が与えられた場合に
どのように Compute Shader を呼び出せばよいのか悩みます。
1. 65535 回を超える場合
x, y, z に分けるにしろ、CS 内のアトリビュートで Group 内スレッド数を増やすにしろ、
実行回数が常に定数で割り切れるとは限らない。
2. 速度
ある程度グループ内のスレッド数を大きめの値にしなければ ComputeShader の実行
速度が落ちます。65535 回未満だからと言って、Dispatch() だけで回数を指定して
下記のようなシェーダーを走らせると非常に低速になります。
[numthreads(1,1,1)] void cmain_loop1( uint3 threadid : SV_DispatchThreadID ) { lmain( threadid.x ); }
●解決案
二通りの手段を考えてみました。
(1) 複数のシェーダーに分ける
numthreads = 32 などグループスレッド数を増やしたものと、numthreads = 1 の
端数を処理するスレッドに分けて実行します。
下のプログラムは 32 で割り切れる回数分 cmain_loop32 を実行し、端数を
cmain_loop1 で処理しています。
例えば 75 個のデータを処理するなら、cmain_loop32 を 2回、cmain_loop1 を 11回分
実行します。
// Compute Shader 5.0 cbuffer offset_T : register( b0 ) { uint threadid_offset; uint thread_total; uint r0; uint r1; }; [numthreads(32,1,1)] void cmain_loop32( uint3 threadid : SV_DispatchThreadID) { lmain( threadid.x ); } [numthreads(1,1,1)] void cmain_loop1( uint3 threadid : SV_DispatchThreadID ) { lmain( threadid.x + threadid_offset ); }
// C++ const int ThreadGroup= 32; int dcount1= data_count/ThreadGroup; int offset= dcount1*ThreadGroup; int dcount2= dcount - offset; offset_T cparam; cparam.threadid_offset= offset; cparam.thread_total= data_count; context.UpdateSubresource( CB_Offset.iBuffer, 0, NULL, &cparam, 0, 0 ); context.CSSetConstantBuffers( 0, 1, &CB_Offset.iBuffer ); if( dcount1 > 0 ){ context.CSSetShader( LoopShader_1.iCS, NULL, 0 ); context.Dispatch( dcount1, 1, 1 ); } if( dcount2 > 0 ){ context.CSSetShader( LoopShader_32.iCS, NULL, 0 ); context.Dispatch( dcount2, 1, 1 ); }
(2) 動的分岐を用いる
端数込みで 32 の倍数分実行します。スレッド番号が実行したい回数より多ければ、
動的分岐で処理を省きます。
例えば 75 個のデータを処理するなら、cmain_loop_dis を 3 回 (96回分) 実行し、
id が 75 以上なら何もしないで終了します。
// Compute Shader 5.0 cbuffer offset_T : register( b0 ) { uint threadid_offset; uint thread_total; uint r0; uint r1; }; [numthreads(32,1,1)] void cmain_loop_dis( uint3 threadid : SV_DispatchThreadID ) { if( threadid.x < thread_total ){ lmain( threadid.x ); } }
// C++ const int ThreadGroup= 32; int dcount1= (data_count+ThreadGroup-1)/ThreadGroup; offset_T cparam; cparam.threadid_offset= 0; cparam.thread_total= data_count; context.UpdateSubresource( CB_Offset.iBuffer, 0, NULL, &cparam, 0, 0 ); context.CSSetConstantBuffers( 0, 1, &CB_Offset.iBuffer ); context.CSSetShader( CSSubDShaderDis.iCS, NULL, 0 ); context.Dispatch( dcount1, 1, 1 );
●実行結果
RADEON HD 5870 で試してみました。
1 フレームあたり 7セット Compute Shader の実行を繰り返しています。
それ以外の描画は fps などのフォントのみ。
上のプログラムはグループ内スレッド数 32 固定でしたが、16~320 まで変更して
試しています。
GroupThread= 16 (1) 476 fps (2) 482 fps GroupThread= 32 (1) 840 fps (2) 852 fps GroupThread= 48 (1) 973 fps (2) 1006 fps GroupThread= 64 (1) 1102 fps (2) 1145 fps GroupThread= 96 (1) 984 fps (2) 1026 fps GroupThread= 128 (1) 1090 fps (2) 1140 fps GroupThread= 256 (1) 1083 fps (2) 1128 fps GroupThread= 320 (1) 1009 fps (2) 1065 fps
GroupThread が小さいと低速です。あまり小さいと Dispatch() の 65535 制限にも
ひっかかります。
どのケースでも、分岐を用いた (2) の方が高速でした。
端数分とはいえ GroupThread=1 で実行しているスレッドがあるため効率が悪い、
7セットの実行中に毎回シェーダー切り替えが発生しているから、切り替えないで済む
(2) の方が条件的に有利、等の理由が考えられます。
関連エントリ
・DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
・Direct3D11/DirectX11 ComputeShader 4.0 を使う
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
引き続きテクスチャ圧縮フォーマット BC6H の詳細構造です。(前回)
8bit を前提にした BC7 と違い、BC6H は 16bit までの HDR コンポーネントを
格納することが出来ます。
BC7 同様 4x4 block を複数の領域に分割出来ますが、分割数は 2までで
分割パターンも 32通りとなります。
エンコード方法も BC7 とは全く異なる構造をしています。
●BC6H のモード
4x4 block 単位で下記の 18通りのエンコードをモードを選択できます。
モード符号は 18種類ありますが、そのうち 4つは未使用。
現在定義されているのは 14種類です。
●BC6H の endpoint
BC6H では subset が 2つの endpoint 直値で構成されている場合と、
color0 からの差分 (offset) で格納する場合の二通り存在します。
BC7 は直値だけでした。
分割モードで差分の場合は、代表カラー1 + 差分3 になります。
・subset (補間ペア) が直値の場合 color0, color1
・subset (補間ペア) が差分の場合 color0, diff1
・subset (補間ペア) が直値かつ領域分割の場合 color0, color1, color2, color3
・subset (補間ペア) が差分かつ領域分割の場合の color0, diff1, diff2, diff3
●endpoint の符号化
endpoint (カラー値) の格納方法はモードごとに異なっており、
さらに格納場所が分散しているケースもあって非常に複雑です。
仕様書には分散する bit 集めて合成するためのテーブル (Table.F) が定義
されています。
このテーブルを読みながらデータを拾い集める必要があります。
反面、partition bit と index bit はどのモードでもほぼ固定の領域が割り当て
られており、こちらは BC7 よりもアクセスしやすいと言えます。
●BC6H の各モード詳細
●パーティションテーブル
分割パターンはプリセットなので、実際にエンコード&デコードする場合は
テーブルが必要です。
テーブルは BC7 と共用で、BC6H は 2分割用 64個種類のうち前半 32個だけ使います。
●bit パターン例
BC7 mode0 partition 0 128bit
BC6H mode0 partition 0 128bit
BC6H の例で U,T,W はそれぞれ g2,b2,g3 の一部ビットです。
5箇所に点在している V は b3 です。
●フォーマット
BC6H/BC7/BPTC は予想より複雑な構造をしています。
それでも格納されるデータの構造がある程度わかれば、エンコードフォーマットを
選択する場合の判断材料として役に立つでしょう。
関連エントリ
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
・OpenGL の圧縮テクスチャ (3) BPTC, BC6H/BC7
・OpenGL の圧縮テクスチャ (2) 法線圧縮
・Android OpenGL ES 2.0 の圧縮テクスチャ
8bit を前提にした BC7 と違い、BC6H は 16bit までの HDR コンポーネントを
格納することが出来ます。
BC7 同様 4x4 block を複数の領域に分割出来ますが、分割数は 2までで
分割パターンも 32通りとなります。
エンコード方法も BC7 とは全く異なる構造をしています。
●BC6H のモード
4x4 block 単位で下記の 18通りのエンコードをモードを選択できます。
モード符号は 18種類ありますが、そのうち 4つは未使用。
現在定義されているのは 14種類です。
BC6H mode bit mode No ---------------------- 00 0 0 01 1 1 00010 2 2 00110 6 3 01010 10 4 01110 14 5 10010 18 6 10110 22 7 11010 26 8 11110 30 9 00011 3 10 00111 7 11 01011 11 12 01111 15 13 10011 19 14 reserved 10111 23 15 reserved 11011 27 16 reserved 11111 31 17 reserved
●BC6H の endpoint
BC6H では subset が 2つの endpoint 直値で構成されている場合と、
color0 からの差分 (offset) で格納する場合の二通り存在します。
BC7 は直値だけでした。
分割モードで差分の場合は、代表カラー1 + 差分3 になります。
・subset (補間ペア) が直値の場合 color0, color1
・subset (補間ペア) が差分の場合 color0, diff1
・subset (補間ペア) が直値かつ領域分割の場合 color0, color1, color2, color3
・subset (補間ペア) が差分かつ領域分割の場合の color0, diff1, diff2, diff3
●endpoint の符号化
endpoint (カラー値) の格納方法はモードごとに異なっており、
さらに格納場所が分散しているケースもあって非常に複雑です。
仕様書には分散する bit 集めて合成するためのテーブル (Table.F) が定義
されています。
このテーブルを読みながらデータを拾い集める必要があります。
反面、partition bit と index bit はどのモードでもほぼ固定の領域が割り当て
られており、こちらは BC7 よりもアクセスしやすいと言えます。
●BC6H の各モード詳細
BC6H mode 0 (No.0) 2分割 32通り endpoint: RGB(10,10,10) + d(5,5,5) x3 index: 3bit (8段階) 2bit mode 指定 25bit R0 10bit + Rn 5bit x 3 25bit G0 10bit + Gn 5bit x 3 25bit B0 10bit + Bn 5bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 1 (No.1) 2分割 32通り endpoint: RGB(7,7,7) + d(6,6,6) x3 index: 3bit (8段階) 2bit mode 指定 25bit R0 7bit + Rn 6bit x 3 25bit G0 7bit + Gn 6bit x 3 25bit B0 7bit + Bn 6bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 2 (No.2) 2分割 32通り endpoint: RGB(11,11,11) + d(5,4,4) x3 index: 3bit (8段階) 5bit mode 指定 26bit R0 11bit + Rn 5bit x 3 23bit G0 11bit + Gn 4bit x 3 23bit B0 11bit + Bn 4bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 6 (No.3) 2分割 32通り endpoint: RGB(11,11,11) + d(4,5,4) x3 index: 3bit (8段階) 5bit mode 指定 23bit R0 11bit + Rn 4bit x 3 26bit G0 11bit + Gn 5bit x 3 23bit B0 11bit + Bn 4bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 10 (No.4) 2分割 32通り endpoint: RGB(11,11,11) + d(4,4,5) x3 index: 3bit (8段階) 5bit mode 指定 23bit R0 11bit + Rn 4bit x 3 23bit G0 11bit + Gn 4bit x 3 26bit B0 11bit + Bn 5bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 14 (No.5) 2分割 32通り endpoint: RGB(9,9,9) + d(5,5,5) x3 index: 3bit (8段階) 5bit mode 指定 24bit R0 9bit + Rn 5bit x 3 24bit G0 9bit + Gn 5bit x 3 24bit B0 9bit + Bn 5bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 18 (No.6) 2分割 32通り endpoint: RGB(8,8,8) + d(6,5,5) x3 index: 3bit (8段階) 5bit mode 指定 26bit R0 8bit + Rn 6bit x 3 23bit G0 8bit + Gn 5bit x 3 23bit B0 8bit + Bn 5bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 22 (No.7) 2分割 32通り endpoint: RGB(8,8,8) + d(5,6,5) x3 index: 3bit (8段階) 5bit mode 指定 23bit R0 8bit + Rn 5bit x 3 26bit G0 8bit + Gn 6bit x 3 23bit B0 8bit + Bn 5bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 26 (No.8) 2分割 32通り endpoint: RGB(8,8,8) + d(5,5,6) x3 index: 3bit (8段階) 5bit mode 指定 23bit R0 8bit + Rn 5bit x 3 23bit G0 8bit + Gn 5bit x 3 26bit B0 8bit + Bn 6bit x 3 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 30 (No.9) 2分割 32通り endpoint: RGB(6,6,6) x 4 index: 3bit (8段階) 5bit mode 指定 24bit R 6bit x 4 24bit G 6bit x 4 24bit B 6bit x 4 5bit partition 指定、32種類 46bit index (3bit x14 + 2bit x 2) BC6H mode 3 (No.10) 分割なし endpoint: RGB(10,10,10) x 2 index: 4bit (16段階) 5bit mode 指定 10bit R 10bit x 2 10bit G 10bit x 2 10bit B 10bit x 2 63bit index (4bit x15 + 3bit x1) BC6H mode 7 (No.11) 分割なし endpoint: RGB(11,11,11) + d(9,9,9) index: 4bit (16段階) 5bit mode 指定 20bit R0 11bit + R1 9bit 20bit G0 11bit + G1 9bit 20bit B0 11bit + B1 9bit 63bit index (4bit x15 + 3bit x1) BC6H mode 11 (No.12) 分割なし endpoint: RGB(12,12,12) + d(8,8,8) index: 4bit (16段階) 5bit mode 指定 20bit R0 12bit + R1 8bit 20bit G0 12bit + G1 8bit 20bit B0 12bit + B1 8bit 63bit index (4bit x15 + 3bit x1) BC6H mode 15 (No.13) 分割なし endpoint: RGB(16,16,16) + d(4,4,4) index: 4bit (16段階) 5bit mode 指定 20bit R0 16bit + R1 4bit 20bit G0 16bit + G1 4bit 20bit B0 16bit + B1 4bit 63bit index (4bit x15 + 3bit x1)
●パーティションテーブル
分割パターンはプリセットなので、実際にエンコード&デコードする場合は
テーブルが必要です。
テーブルは BC7 と共用で、BC6H は 2分割用 64個種類のうち前半 32個だけ使います。
●bit パターン例
BC7 mode0 partition 0 128bit
H---------------------------------L g0 r5 r4 r3 r2 r1 r0 Pt M GGGrrrrR RRRrrrrR RRRrrrrR RRRPPPP1 bit 31..0 b2 b1 b0 g5 g4 g3 g2 g1 BBBbbbbB BBBggggG GGGggggG GGGggggG bit 63..32 ___--___ ---__ P-bit b5 b4 b3 IIIiiIII iiiIIPPP PPPbbbbB BBBbbbbB bit 95..64 __---___ ---___-- -___---_ __---___ iiIIIiii IIIiiiII IiiiIIIi iiIIIiii bit 127..96
BC6H mode0 partition 0 128bit
H---------------------------------L b0 g0 r0VTU M BBBBBBBG GGGGGGGG GRRRRRRR RRRvtu00 bit 31..0 b2V b1 g3V g1 g2W r1 BBBvbbbb bGGGGvgg gggGGGGw rrrrrBBB bit 63..32 __---___ ---___ Pt V r3V r2 IIiiiIII iiiIIIPP PPPvrrrr rvRRRRRB bit 95..64 __---___ ---___-- -___---_ __---__ IIiiiIII iiiIIIii iIIIiiiI IIiiiIII bit 127..96 U=g2, T=b2, W=g3, V=b3
BC6H の例で U,T,W はそれぞれ g2,b2,g3 の一部ビットです。
5箇所に点在している V は b3 です。
●フォーマット
BC6H/BC7/BPTC は予想より複雑な構造をしています。
それでも格納されるデータの構造がある程度わかれば、エンコードフォーマットを
選択する場合の判断材料として役に立つでしょう。
関連エントリ
・Direct3D11/OpenGL 圧縮テクスチャ BPTC, BC6H/BC7 の詳細構造 (1)
・OpenGL の圧縮テクスチャ (3) BPTC, BC6H/BC7
・OpenGL の圧縮テクスチャ (2) 法線圧縮
・Android OpenGL ES 2.0 の圧縮テクスチャ