Game Developers Conference 2009現地レポート

西川善司の3DゲームファンのためのDirectX 11講座(中編)
非ゲーマーのアナタのパソコンも
DirectX 11演算シェーダーでスーパーコンピューターに変身する!

「Microsoft game Developer Day」の様子

3月23日~27日開催(現地時間)

会場:サンフランシスコ Moscone Center



 前編でレポートしたようにDirectX 11は、Windows 7よりも前にWindows Vista向けにリリースされることが確実視されてきたが、悲しいことにDirectX 11提供当初は、DirectX 11対応GPUがないかもしれないということだ。その代わりDirectX 11は、DirectX 10.x対応GPU、DirectX 9対応GPUのサポートを公言しており、特にDirectX 11環境下のDirectX 10.x対応GPUでは、DirectX 11の目玉機能である演算シェーダーが利用できることが公式に宣言された。

 言ってみれば、DirectX 10.x対応GPUは、実質的に延命がなされたことになる。ただ、裏を返せば、DirectX 11対応GPUの提供時期がまったく不透明になったことをごまかす意味合いも強い。いずれにせよ、今手持ちのDirectX 10.x対応GPUはしばらく現役でいられると言うことだ。

 中編となる今回は、DirectX 11の新要素である演算シェーダーについて細かく見ていくことにする。DirectX 11のもう1つの新要素「テッセレータ」については後編にてレポートする。



■ Windows環境下の標準GPGPUプラットフォームになりうる「演算シェーダー」

 最近、GPGPUというキーワードを耳にすることが多くなってきただろう。GPGPUとはGeneral Purpose GPUの略で、すなわち「GPUの汎用的活用」を意味する。

 2001年、21世紀になって登場したグラフィックステクノロジーをソフトウェアで実装するという新発想「プログラマブルシェーダー」アーキテクチャが劇的に進化し、その性能向上のためにGPU内の演算リソースの爆発的増加があり、2008年時点のGPUは、1基でTFLOPS(テラフロップス:1秒間に1兆回の浮動小数点数演算が行なえる性能指標値)の性能を身につけるまでに至っている。ちなみに、CPUはマルチコアCPUでも1基当たり数十~百数十GFLOPS止まりで、GPUの1/10以下前後。

 このGPUを「3Dシューティングゲームのフレームレート向上だけのために使うのはもったいなさすぎる」ということから、GPUを汎用目的に使おうというムーブメントが起こった。これがGPGPUだ。

 ただしGPUは、もともと3Dグラフィックス専用のために設計されていたために、一口にGPGPUとはいっても、その実現はそのGPUごとのアーキテクチャに依存してしまいがちで、1つのGPGPUソフトウェアを広く活用することが困難だった。

 PCも、Intel x86CPUとMicrosoft Windowsという標準プラットフォームが確立されたからこそ、ソフトウェアの頒布性が広がったわけで、GPGPUにも標準プラットフォームが望まれてきたのであった。

 これにいち早く動きを見せたのがNVIDIAで、「CUDA」(Compute unified device architecture)というGeForce、QuadroといったNVIDIA自社GPU向けGPGPUプラットフォームを2006年より提供し、一定の成功を収めている。競合のATI(AMD)は対抗技術の「ATI Stream」をぶつけるも、後発ゆえか認知度やアプリケーションの登場率でCUDAに及ばないという状況だ。

 「ATI対NVIDIAの図式がGPGPUにもあるだけ」という見方がなされることがあるが、それは少々認識が誤っている。

 少なくとも3Dグラフィックスにおいては、Direct3D(DirectX)という標準APIコンポーネント(プラットフォーム)があるので、Windows環境下でDirectXベースで動作する3DグラフィックスはATIでもNVIDIAでも動作できる。ATI対NVIDIAの図式は、あくまでその実行パフォーマンスでの話だ。

 GPGPUの場合は、組んだGPGPUプログラムがATIかNVIDIAのどちらかでしか動かない、踏み絵的な「ATI対NVIDIA」なのだ。

 これではGPGPUの進化、発展、普及が望めない。そこでGPGPUの標準化プラットフォームが強く望まれるようになった。

 このGPGPU標準プラットフォームを、Windows環境下で提供しようと出てきたのがDirectX 11ということになる。

 なおDirectXは、Microsoft/Windows環境下のAPIコンポーネントなので「非Microsoft/Windows環境下のGPGPUはどうすればいいのか?」という話にもなるわけだが、これは非Microsoft/Windows環境下向けのグラフィックスAPIを統括しているKhronosグループが、ちゃんと対抗馬として「OpenCL」(Open Computing Language)をApple主導の下で規格策定中だ。なおOpenCLは、次期Mac OSのSnow Leopardに搭載される予定。

 話を戻すとDirectX 11は、このGPGPUの標準プラットフォームとして「DirectX Copmute Shader」(演算シェーダー)を提供する。

 余談だが、「演算シェーダー」という和名呼称は、マイクロソフト日本法人が2008年9月に開催したゲーム開発者向けカンファレンス「GameFest」の、日本語版プレゼンテーションにて初めて用いられた呼び名だ。今回Microsoft関係者に確認したところ、正式名を「演算シェーダー」とするか「コンピュート・シェーダー」にするかはまだ決めかねているとのこと。これまでも、「頂点シェーダー」と「パーテックス・シェーダー」をどちらを正式和名として定着するか揺れたことがあったが、用語としては頂点シェーダーが定着してしまった経緯がある。

 本稿では、特に理由はないが「演算シェーダー」を用いることにする。

■ DirectX演算シェーダーはDirect3Dに統合された形で提供される

NVIDIA Simon Green氏
演算シェーダーでなにができるのか
Direct3Dと統合されている演算シェーダー

 まず、演算シェーダーでなにができるかについて。

 GPUは3Dグラフィックスを描画するためのものだったわけだが、これを根本的な処理内容だけに着目すると「大量のデータに対して一定のベクトル演算処理を行なって出力すること」に相当する。これは突き詰めて言えば「データ並列コンピューティング」に相当する。3Dグラフィックス描画は、コンピューティングパラダイム的視点で見るとデータ並列コンピューティングなのだ。

 データ並列コンピューティングといえば、天候シミュレーション、地質学シミュレーション、医療シミュレーションなどの各種シミュレーションがその代表格だが、もっと民生向けなテーマとしてはさまざまな画像処理、映像認識などもある。ゲームに近いテーマで言えば、物理シミュレーションやAIなどもデータ並列コンピューティングのテーマだ。

 3Dグラフィックスの様々なポスト処理もデータ並列コンピューティングであるし、レイトレーシングのような、ポリゴンベースのリアルタイム3Dグラフィックスとは別の切り口のCG技法の実装も「GPGPUで行ける」と目されている。

 演算シェーダーはDirect3Dに統合される形で提供され、演算シェーダー専用のAPIが提供されるわけではない。「Direct3Dの新しい機能」という形で提供され、実質的にはDirectXのシェーダープログラミング言語「HLSL(High Level Shader Language)」の進化形という位置付けになる。NVIDIA CUDAやATI StreamのようなGPGPU専用のコンポーネントではなく、Direct3Dに統合されているので、Direct3D管理下のすべてのリソースが透過的にアクセスできる。つまり、3Dグラフィックスとの親和性が高いと言うことができる。

 ここには賛否があるだろうし、逆に演算シェーダーの特徴ということができる。Direct3Dで描画した3Dグラフィックスを演算シェーダーで処理したり、演算シェーダーで生成したシミュレーション結果を元に3Dグラフィックスを描画するといった相互連携は図りやすい。

 計算に用いるシェーダーユニット自体はピクセルシェーダーも演算シェーダーも同一であるので、GPU的な視点で見ると、データに3Dグラフィックス的な意味合いを持たせて動かしたときがピクセルシェーダーであり、データをあくまで汎用データとして処理させたときが演算シェーダーであるということもできる。

 この概念を図として表したのが下図だ。

 前編で述べたように、緑色のマスで描かれているのが、DirectX 11で新設された新しいシェーダーステージだ。3Dグラフィックスに関わる流れが左に描かれており、3Dグラフィックスに限定されないプログラマブルシェーダーとして右側に切り離されたようにして描かれている演算シェーダー(Compute Shader)ということになる。

DirectX 11(Direct3D 11)のパイプライン概念図

■ DirectX 11演算シェーダーのバージョンは4.0/4.1/5.0の3タイプ

 そして前編から繰り返し言っているように、DirectX 11はDirectX 10.x対応GPUにも対応しており、演算シェーダーが利用できるようになる。

 ただ、DirectX 11環境下でのDirectX 10対応GPUの演算シェーダーは、DirectX 11演算シェーダーのサブセットという位置づけになる。言い換えるならば下位仕様版ということになる。

 ここがまた複雑なのだが、この切り分けの関係上、演算シェーダー(Compute Shader:CS)のバージョン分けが規定される。

 ややこしいのだが、DirectX 11環境下でDirectX 10.0対応GPUの演算シェーダーのバージョンはCS4.0となり、同じくDirectX 11環境下でDirectX 10.1対応GPUの演算シェーダーのバージョンはCS4.1となる。

 「DirectX 11にて初めて演算シェーダーが実現されたのに、いきなりそのバージョン番号がCS4.0とCS4.1なのはなぜか?」という質問が上がりそうだが、これは、DirectX 10.0のプログラマブルシェーダー仕様(Shader Model:SM)が4.0だったことから来ている。同様にDirectX 10.1はSM4.1だったので、演算シェーダーのバージョンもCS4.1となったというわけだ。

 DirectX 11でのプログラマブルシェーダー仕様は5.0となるので、演算シェーダーもこのルールに従ってCS5.0となる。

 DirectX 10.0発表時「バージョン番号の混乱が起こらないように配慮していきたい」といっていたのにもかかわらず、DirectX 11では早速ややこしいことになった。

 余談になるが、「下位版が幅をきかせると上位版が絶対に普及しない」というDirectXのこれまでの繰り返された歴史に則れば、CS5.0は普及しないかもしれない。DirectX 11対応GPUの登場が先送りされ、DirectX 10.x対応GPUの延命が約束された今、まさにこのルールに乗ることになりCS5.0の立場は危うい。

 「演算シェーダーの登場のせいで、NVIDIA CUDA、ATI Streamの立場は?」と心配されたが、もしかすると、ATIやNVDIAにとってここにつけいる隙があると言えるかもしれない。

DirectX 11演算シェーダーをDirectX 10.x世代GPUで動かすと演算シェーダー4.xとなるDirectX 11演算シェーダーのバージョンごとの仕様の違いDirectX 11演算シェーダー4.xは制約も多い

■ 演算シェーダーにはスレッドグループで共有されるローカルメモリが与えられる!

AMD Richard Huddy氏(Developer Relations Manager, AMD)は、「演算シェーダーのすごさをダンスで表現すると……」と言って踊り出し来場者の爆笑を誘った。氏は毎回、自社技術をギャグや笑いで表現することで有名。筆者は、氏から踊りがちゃんと撮影できたかどうか確認されたほど
演算シェーダーの命令セットは同バージョンの頂点シェーダーベースに
演算シェーダー5.0の仕様概要

 演算シェーダーの仕様についてみていくことにしよう。

 演算シェーダーの命令セットは、頂点シェーダーの命令セットを引き継いでいる。演算シェーダー4.0/4.1/5.0はそれぞれ、頂点シェーダー4.0/4.1/5.0をベースにしていると言うことだ。

 演算シェーダーはDirect3Dに統合されているとはいえ、実行はグラフィックスパイプラインとは独立して実行ができる。

 DirectX 11では、GPUへの処理発注をジョブ・キュー型のマルチスレッド制御できる仕組みが導入されるので、GPU側の都合をあまり気にせずに演算シェーダーの処理を発注できる。ただしそうはいっても、GPUのアーキテクチャによっては、3Dグラフィックスの処理とGPGPUの処理とをGPU内部で同時処理できない場合もあるため、パフォーマンスチューニングは必要不可欠だとのこと。特にDirectX 10.x世代GPUでの演算シェーダーは、グラフィックス処理と排他的動作しかできない場合があり、ゲームなどのリアルタイムアプリケーションにおける演算シェーダーの活用タイミングは慎重にデザインすべきだろう。

 まずは演算シェーダー5.0の方から見ていこう。

 GPGPU向けの演算シェーダーとはいえ、テクスチャー・サンプリング、テクスチャーフィルタリングの命令は利用できる。グラフィックス系シェーダーと違うのは、入力と出力の個数が固定されていないという点。これは、CPUのプログラムモデルに近いものを実装できることを想像させる。

 プログラムの実行はCPUプログラムとはだいぶ異なる。

 演算シェーダー用のプログラムをスレッドとして定義して、ある処理したい膨大なデータに対して、同時に何スレッドで取りかかるか……のような実行計画書的なものを定義して、実行部隊(GPU)に投げることで初めて実行される。

 概念的なたとえ話だが、8×8=64のデータがあったとして1個ずつデータを処理する演算シェーダーを64スレッド起こして取りかかるのか、4×4個ずつ処理するような演算シェーダーを4スレッド起こして取りかかるのかを、自由に定義できるということ。処理内容や処理対象のデータ構造に応じてここは自在にカスタマイズができる。

 このスレッド管理/制御は、x、y、zの3次元配列構造のスレッドグループで行なうことができる。データ処理を適当な実行単位で区切って、その都度同期を取ったり、あるいは中間時点の結果に応じて適応型の場合分け処理をしてからさらに取りかかるような、複数ステップで処理を行なう場合などには、こうした多次元スレッドグループ管理は都合がよい。

 スレッド数は最大1,024個まで。ただしx×y×z≦1024,z≦64という制限が与えられている。

演算シェーダースレッドの実行は、3次元配列構造のスレッドグループに分けて管理可能どうグループを分けてどう実行させるかは、処理内容や処理の同期取りタイミングによって自在にカスタマイズ可能

 各スレッドグループには、そのグループに属するスレッド間で共有されるスレッド・ローカル・ストレージ(TLS)と呼ばれるスクラッチパッドメモリ(または多目的キャッシュメモリ)が32KB与えられる。これは各スレッド間のやりとりに必要な中間データの書き出しや、すべてのスレッドで共有しそうな共通データの格納場所として多目的に利用できる。

 これはレンダリング結果のポスト処理などにおける、複数ピクセル(テクセル)の反復読み出しによる無駄な帯域消費を劇的に低減できるとして期待されている。

 例えば“ぼかし”処理の定番「ガウスブラー」(Gaussian Blur)などでは、ある着目しているピクセルとその周辺の複数ピクセルを読み出して、それらから平均値を計算して出力バッファに書き出していく。1つ終わったらその隣へという具合に順番に処理していく関係上、今着目しているピクセルの周辺ピクセルは、1つ前の処理でも間違いなく読み出されている。読み出す情報そのものが同じなのに、そのたびにメモリバスを消費していたのでは効率が悪すぎる。

 GPUのアーキテクチャ上、こうした無駄な処理が頻発することが予測されているため、テクスチャーキャッシュなどの仕組みを導入して無駄な帯域消費の低減メカニズムを実践してはいるが、これが複数のスレッドで頻発すれば対応しきれずキャッシュ・スラッシングが起きてしまう(キャッシ機構の役割をろくに果たせずキャッシュの内容がどんどん変わってしまう現象)ので、根本的な解決にはなっていない。

演算シェーダーを用いてのガウスブラーの実装例ガウスブラーは、縦方向のぼかしと横方向のぼかしを分けて実装するのが定石
横方向をぼかす。この例では、着目しているピクセルの周辺4個を参照して平均値を求める。この際、新しく読まれるのは1個のみで他3つは以前読み出したものと同じ。つまり、4個の読み出しのうち3個が無駄なメモリアクセスとなる縦方向についても同様
演算シェーダーを使えばスレッドグループごとにTLSを活用することで、この無駄な重複読み出しを完全解消できるつまり今回の4×ガウスブラーの処理で言えば、演算シェーダーを使うとピクセルシェーダーを使ったときの4倍も帯域消費を抑えられる。これはパフォーマンスアップに直結する!……というシナリオ

 演算シェーダーならば、スレッド定義をした段階でどういう反復読み出しが行なわれるのかが予測可能であり(というよりも、それを前提にしてスレッド設計をしているはず)、その反復読み出しが行なわれるデータをTLSにあらかじめ読み出してしまえば、テクスチャーキャッシュに頼るよりも確実なメモリ帯域節約が行なえる。いわば、キャッシュの活用を手動制御するようなイメージだ。

演算シェーダーには、スレッドグループで共有されるローカルメモリ(TLS)が搭載される演算シェーダー5.0のTLS容量は32KB

 一方演算シェーダー4.xでは、基本コンセプトは演算シェーダー5.0と同じだが、もともとDirectX 10世代GPUなのでDirectX 11演算シェーダーのフル仕様は動作できない。いくつかの仕様制限がある。

 まず、スレッド実行の最大数は768とやや少ない(5.0では1,024)。また、3次元配列構造のスレッドグループ管理は行なえるがx、y、zのうち、zはz=1に限定されるので実質的には2次元配列構造まで。TLSは半分の16KBまでだ。

演算シェーダー4.xの仕様制限

 このほか、演算シェーダー4.xと5.0を比較した場合の格差としては以下のようなものがある(※Atomic命令、UAV、倍精度などについての詳細は前編を参照のこと)。

●Atomic命令(不可分操作命令)がない
●バッファのAppendとConsumeに未対応
●UAVは単一出力に限定
●64ビット倍精度浮動小数点の未サポート
●マルチスレッド実行制御の一部未サポート

 演算シェーダー4.xでも、UAVが単一出力ながらサポートされるので拡散系のフィルタ処理は実現できるし、TLSが利用できるので途中で処理同期を取りながらのデータ並列コンピューティングも実現できる。これはテクスチャーを書き出して読み直すよりも圧倒的に高速だ。演算シェーダー4.xでも、GPGPUの醍醐味のほとんどが提供されると考えていいだろう。

 Simon Green氏によれば、NVIDIAのDirectX 10世代GPU(GeForce 8000/9000/GT200シリーズ)では、スレッド数を多くするとパフォーマンスが上がらないとか、スレッドごとにアクセスできるTLSの範囲が制限されるといった制約があるとしていた。

 また現行DirectX 10世代GeForceでは、演算シェーダーとピクセルシェーダーのモード切替に時間を要するため頻繁にモードを往来するとパフォーマンス的に辛くなるだろうと警告し、ゲームであれば演算シェーダーの活用(GPGPUモードの活用)は1フレーム内で1回程度にすべきだと述べていた。

 もちろんこうした制約は、初めからDirectX 11での動作を前提としたDirectX 11世代GeForceは解消されるに違いない。

演算シェーダー4.x利用の注意点

■ 演算シェーダーがデータ並列コンピューティングをスタンダライズするのか

 この演算シェーダーの考え方は、ATI StreamやNVIDIA CUDAなどのアーキテクチャ依存のGPGPUの最大公約数的なコンセプトになっており、それぞれにとっては「うちはもっとできる子なのに……」という不満は出てきそうだが、現状最もリーズナブルな仕様ということはできる。

 なにより、GPGPUにおいての「ATIをとるか、NVIDIAをとるか」の踏み絵がなくなる点で一般ユーザーにとってはおいしい。また開発者にとっても、自分のGPGPUアプリケーションが、広く活用してもらえることになるので嬉しい。Direct3Dで統合されていることは制約と映る場合もあるが、「使い道がなさそうならば3Dグラフィックスのための支援ツール的に使えばいいじゃないか」という逃げ道も用意されており、実にMicrosoft的、合理的な判断だと個人的には思っている。

 またDirectX 11リリース後は、DirectX 10世代GPU、DirectX 11世代GPUが搭載されたパソコンのすべてで演算シェーダーが動作できるようになるので、かなりの割合のパソコンが「データ並列コンピューティング・READY」となる。そうなった時ゲーム開発者たちは、本腰を入れてデータ並列コンピューティングをゲームの本質的な進化に使えないかの模索を始めることだろう。

 例えば物理シミュレーションについても、これまでのようなゲームの本質に影響しない効果物理だけの実装ではなく、ゲーム性の根幹に関わるような物理シミュレーションの組み込みを行ない始めるかも知れないし、AIについても現在のスクリプトベースのステートマシン的なAIから、現在の自分の状況把握と次の状況予測を複数行なって、計算結果の大半を捨てるのも惜しまずに最適な行動を選択していくロボティックス的なアプローチのAIを、ゲームに実装していくこともできるようになるかも知れない。

 やや妄想が過ぎた感もあるが、Windows環境下のGPGPUプラットフォームとして、DirectX 11提供後、演算シェーダーは高い地位を確立することだろう。

 DirectX 11演算シェーダーの普及後、ATI Stream、NVIDIA CUDAといったハードウェア依存型GPGPUがどのような動きを見せるかも注目される。


(2009年3月26日)

[Reported by ]