Game Studio 4.0におけるBasicEffectの最適化
Game Studio 4.0で、BasicEffect API自体は変わっていませんが、今までより更に最適化が進んでいます。
以前のバージョンでのBasicEffectはシェーダープログラムの手始めとして初心者向けに設計されたものでした。なぜならシェーダープログラムの知識のある人達は直ぐに自前のシェーダーを書くだろうと思っていたからです。ですから、BasicEffectの最適化には多くの時間を割かず、WindowsとXbox 360上でそれなりの速度がでる程度の最適化しかしていませんでした。
Windows Phone対応によって、この優先度は以下の二つの理由によって変更されました。
- 携帯用GPUはWindowsやXbox 360より遅く、シェーダー命令ひとつ削除するだけでも大きな速度差がある
- カスタムシェーダー未対応なので、BasicEffectにはできる限りの最適化を施す必要がある
シェーダーの組み合わせ
その名前にも関わらずBasicEffectの実装は複雑です。公開されているHLSLソースコードを見れば分かりますが、以前のバージョンでは12の頂点シェーダーが以下のパラメーターの組み合わせに対応しており、
- ライティング: なし、頂点ライティング、ピクセル単位ライティング
- 頂点カラー: なし、あり
- テクスチャ: なし、あり
4つのピクセルシェーダーが以下の組み合わせに対応しています。
- ピクセル単位ライティング: なし、あり
- テクスチャ: なし、あり
BasicEffectには複数のパラメーターがありますが、これら全ての組み合わせごとに最適化はしていませんでした。例えばフォグ計算は常にしており、FogEnabledプロパティによってパラメーターの値を変えてフォグの有無を再現しているだけでした。
私たちは最適化された複数のシェーダーを提供する(GPU命令数を少なくする)のと、少数のシェーダー(メモリオーバーヘッドを減らし、開発/テストコストを抑える)との間でバランスを取らないといけません。私達がWindows Phone向けに、このバランスを再検討した結果、最適化されたシェーダーを追加することにしました。その結果、XNA Game Studio 4.0のBasicEffectは32のシェーダーの組み合わせを持つことになりました。
頂点シェーダーは以下の20の組み合わせがあります。
- ライティング: なし、頂点ライトx1、頂点ライトx3、ピクセル単位ライティングx3
- 頂点カラー: なし、あり
- テクスチャ: なし、あり
- フォグ: なし、あり(fog=offはライティングなしのときのみ)
これに加えて10個のピクセルシェーダーがあります。
- ライティング: なし、頂点ライティング、ピクセル単位ライティング
- テクスチャ: なし、あり
- フォグ: なし、あり(fog=offはピクセル単位ライティング以外のときのみ)
つまり:
- 以前のバージョンではフォグの有無は速度に関係なかったが、4.0ではフォグ無しの方がちょっと速い
- ひとつの頂点ライティング(PreferPerPixelLighting=false, DirectionalLight0.Enabled=true, DirectionalLight1.Enabled=false, DirectionalLight2.Enabled=false)を使うほうが、は三つのライトを使用しているときより速い
プリシェーダー
シェーダープログラマーが気をつけなければいけないことのひとつとして、綺麗で使いやすいEffectパラメーターを設計した場合、これらのパラメーターフォーマットが常にHLSL最適化に向いたものにならないということです。
D3Dの場合、”preshader(プリシェーダー)”と呼ばれる機能を使って最適化を試みます。HLSLコンパイラーが計算結果が全ての頂点、ピクセルで同一となる部分を抜き出し、この部分を描画直前にCPUで実行するという仕組みです。この仕組みは素晴らしいのですが、幾つかの弱点があります。
- HLSLコンパイラが常に最適化できる部分を探し出せるわけではない
- プリシェーダーの仮想マシン自体は最適化されていない
- プリシェーダーはXbox 360とWindows Phoneでは対応していない
Game Studio 4.0では、このプリシェーダーと同等の機能をC#で実行できるようになりました。新しく追加された以下のメソッドをオーバーロードすることで、このメソッドはEffectPass.ApplyがパラメーターをGraphicsDeviceに設定する直前に呼び出されます。
protected override void OnApply()
この新しい仕組みによって、BasicEffectではプロパティがどのようにHLSLパラメーターに割り当てられているかを気にすることなく、BasicEffectに必要なプロパティを用意することができます。プロパティが変更時に、私たちは単にダーティフラグを設定するだけです。後でOnApplyメソッドが呼ばれたときに、このダーティーフラグの結果によって必要な分だけのHLSLパラメータを計算、設定しています。この機能を使って私たちは他にも以下のパラメーターを計算しています。
- World、View、そしてProjectionをまとめたWorldViewProj行列
- ライティングが有効になっている時にはWorldInverseTranspose行列の計算。これはx,y,zのスケール値が違う行列を使った場合に法線がおかしくなることを防ぐためのもの。以前のバージョンでは対応していなかった。
- View行列から視点位置の取得
- シェーダー内で内積ひとつでフォグの係り具合を決定するベクトル値をFogStart、FogEnd、World、そしてViewから計算
- DiffuseColor、EmissiveColor、AmbientLightColor、そしてAlphaプロパティ値を計算しやすい形にまとめる
命令数の削減
更に、数式の簡略化をし、行列計算を使って三つの平行光源計算を一度にする最適化も施しました。読みづらくなってしまいましたが、命令数はより少なくなりました。
また、フォグの計算方法を変えました。以前は視点から頂点までの距離によってフォグの掛かり具合が変わるDistance Fog(フォグの境界線が曲線になっているところに注目)を使用していました。4.0では単純な深度フォグ、つまり頂点のZ値によってフォグの掛かりかたが変わります。見た目の変化は少ないのですが、深度フォグの方が計算量は遥かに少なくなっています。
結果
最適化の一例として、ライティングなし、フォグなし、頂点カラーとテクスチャありの場合の使用命令数を比較すると以下のようになります。
頂点シェーダー | ピクセルシェーダー | |
XNA Game Studio 3.1 | 30 | 6 |
XNA Game Studio 4.0 | 6 | 3 |
質問される前に自分で質問: 「ソースをZipでくれ」
もちろん、公開したいねぇ。でも、まだ公開する準備を始めていないので、追って連絡があるまで待たれよ!
原文: