Gamefest Japan 2008 デモプログラム


2010/9/17 追記: XNA Game Studio 4.0のサンプルをhttp://higeneko.net/hinikeni/sample/xna40/GamefestJapan2008Demo.zipにアップしました。変更点は「サンプルコードをXNA 4.0向けに更新」に書いてあります。

2010/05/16 追記: マテリアルバッチ描画時のDrawIndexedPrimitiveメソッドへの引数設定が間違っていたのを修正しました。

2009/06/25 追記: XNA GS 3.1用のサンプルを http://higeneko.net/hinikeni/sample/xna31/GamefestJapan2008Demo.zipにアップしました

2008/12/26 追記: デモプログラムの更新、キーボード入力で操作できるようになりました。

やっと終わりました

今年の9月に行われたGamefest Japan 2008のプレゼンテーション資料が先月公開されました。

http://msdn.microsoft.com/ja-jp/xna/cc723908.aspx

私も僭越ながら2つのセッションを担当させて頂き、そのプレゼンテーション資料も公開されています。XNA Game Studioでのゲーム開発においてのパフォーマンス改善について戦略的手法と実践的な手法、主にシステムレベルでの最適化の効果の程を紹介させていただきました。

http://download.microsoft.com/download/0/4/2/04291420-033b-4f46-b87d-de64a2dc6bac/X5.zip

本当はプレゼン時にサンプルプログラムの配布したかったのですが、本業の方のXNA Framework作業の方が佳境の最中で、その合間に書いたコードだったので非常に汚いコードであったのと、先月のXNA Game Studio 3.0のリリースまでその忙しさが続いたので、なかなか公開することができませんでした。っていうか、コメントを追加するのは当然として、他にも3.0対応にしたり、再利用可能なコンポーネントを使いやすく書き換えたりして、発表時のコードよりも4,000行近く増えてしまったのも、今まで時間が掛かった原因でした。

ともかく、なんとか人に見せられる程度の体裁は整えたので、デモプログラムを公開します。

http://higeneko.net/hinikeni/sample/GamefestJapan2008Demo.zip

解凍したフォルダの中には以下の3つのソリューションファイルがあるので、好きなソリューションファイルを使ってください。いずれのプロジェクトを開くのにもXNA Game Studio 3.0が必要です。また、Windowsで実行するにはシェーダーモデル2.0以上のビデオカードが必須になります。

  • DemoWin.sln
    • Windows用のソリューションファイル
  • DemoXbox.sln
    • Xbox 360用のソリューションファイル
  • Demo.sln
    • Windows/Xbox 360の両方のプロジェクトが入ったソリューションファイル

フォントは私がプログラムする時に使っている等幅フォントのConsolasを使用しています。Consolasフォントが無い場合はビルドエラーになるので、以下のURLからConsolasフォントをダウンロードするか、フォント名を変更するようにしてください。

https://www.microsoft.com/downloads/details.aspx?familyid=22e69ae4-7e40-4807-8a86-b3d36fab68d3&displaylang=en

このサンプルプロジェクトのコード、アセットは非商用、商用に関わらず、自由に使ってかまいません。ただし、一部のコードはクリエータークラブオンラインのMesh Instancingのコードを流用、または変更しているので、それらのコードのライセンスについては付属されるMicrosoft Permissive License.rtfに沿います。

http://creators.xna.com/en-US/sample/meshinstancing

で、お約束ですが、このサンプルコードやアセットを使用したことで発生したいかなる問題にも当方は責任を負いません。

デモの内容

デモを起動すると、おなじみのCornflowerBlueが表示されます。ここでコントローラーのスタートボタンで4種類のデモの切り替えをし、Aボタンで手法の切り替えをします。また、デモによっては左右のトリガで表示オブジェクト数の増減ができます。以下に紹介する測定結果は全てXbox 360上で測定したものです。パフォーマンス測定をする時にはビルド設定をリリースにし、デバッグにアタッチしていない状態(Ctrl+F5)で実行することを忘れないでください。

操作方法

  • デモの切り替え
    • スタートボタン、またはEnterキー
  • 手法の切り替え
    • Aボタン、またはスペースキー
  • オブジェクト数の増減
    • 左右のトリガ、またはUp/Downキー
  • デモの終了
    • Backボタン、またはEscapeキー

配列デモ

.Netには様々な配列のタイプがあり、慣れていないとどれを使ったら良いのか悩むと思います。このデモでは、様々な配列を使って5,000個のスプライトの移動させるときに掛かる時間を測定しています。画面左下にある青いバーが更新時間に掛かった時間でログ表示中の"Update"です。

itoxe-2

この配列デモの結果を見るとArrayListを使うのは避けるべきで、それ以外の手法による速度差は30~40%程度なので、後に続くデモの効果に比べると少ないものなので、用途に合わせて使いやすい配列を使いましょうということになります。

このデモのソースコードはDemo/ArrayDemoフォルダの中にあります。

 動的頂点バッファデモ

動的頂点バッファの扱いはXNA GSでは簡単になっているものの、CPUの書き込みとGPUの読み込みがどんなタイミングで衝突するのかを理解して正しく使わないと思わぬパフォーマンス低下の原因になります。また、2Dゲームのパフォーマンス問題として頂点変換をはじめとするシェーダーでするべき仕事もCPUでしてしまい、CPU速度の遅いXbox 360では速度が出ないという問題があります。

このデモでは、動的頂点バッファの使い方と、シェーダーを使った場合の効果を紹介しています。スケール、回転といった基本的なパラメーターを持ったパーティクルを3,000個描画したときに掛かる時間を測定しています。

itoxe-3

CPUで頂点変換処理をした時に比べて、単純なシェーダー処理をするだけで8.5倍、vfetchを活用すると30倍、更にDirectMappingを使うと142倍という桁違いな速度アップになります。ただし、比較対象のCPUで頂点変換するコードは全く最適化されていないので、実際にはそこまで大きな数字は出なくとも0.09ミリ秒で3,000個のパーティクルの描画命令を発行できるというのは大きいと思います。

世の中ではマルチコア、マルチスレッドが騒がれているので、どうしてもマルチスレッドプログラミングに手を出したくなってしまいますが、Xbox 360上でXNA GSが使えるHWスレッド数は4つまでなので、どんなにうまくプログラムしたとしても元の4倍以下の速度にしかならないことを考えるとシェーダーの活用がいかに効果的な手法なのかが判ると思います。

このデモのソースコードはDemo/GameVertexフォルダの中にあり、シェーダーコードはDemo/Content/GameVertexの中にあります。

インスタンスモデルデモ

Xbox 360上で数万ポリゴンのモデルが数十個描画できるなら、数百ポリゴンのモデルを数千個描画できるはずと思って試してみると、数百個も描画すると処理落ちしてしまうという問題を何度か聞いたことがあります。これは、XNA GSというより、Direct X、というよりも昨今のGPUで共通の問題で、この問題を解決する為の手法がインスタンス(Instancing)です。Xbox 360はもちろん、Windowsでも非常に効果的な手法の一つです。

インスタンスモデルのサンプルはクリエータークラブオンラインにもありますが、このデモではWindowsで使えるHWインスタンスをXbox 360でvfetchを使って実装していて、InstancedModelクラスを介してWindowsとXbox 360の両方で全く同じゲームコードを記述することができるようになっています。ただし、シェーダーコードのフェッチ部分はXbox 360用のコードが必要になりますが、フェッチ部分のみなので他のシェーダー処理部分は共通で使えます。

このデモでは、3つのスペキュラ付き平行光源処理をした、12ポリゴン、24頂点のモデルを500個描画したときの時間を測定しています。

itoxe-4

最適化されていない状態とHWインスタンスを使った場合で50倍、DirectMappingを使うと115倍の速度になっています。このデモでは3万6千個のボックスを描画した時点で60fpsを切りますが、これはボックスを移動する更新部分がネックになりGPUにはまだ余裕があります。そこで、試しに1,245ポリゴンのモデルを表示してみたのですが、その場合は最大3,440個のモデルが表示できました。これは1フレームあたり428万ポリゴン、秒間で2億5千万ポリゴンを描画できることになります。

このデモのソースコードはDemo/GameModelフォルダの中にあり、インスタンスモデルクラスの実装はDemoTypesフォルダの中にあり、シェーダーコードはDemo/Content/GameModelの中にあります。

インスタンスモデルアニメーションデモ

シェーダーモデル 3.0の機能として浮動小数点テクスチャと頂点シェーダーからテクスチャを読み込む頂点テクスチャがあります。このデモでは、アニメーションデータを浮動小数点テクスチャに格納し、アニメーション処理をCPUではなくGPUでしています。CPU側の処理としてはオブジェクトの移動と、アニメーションフレームの更新です。それぞれのオブジェクトのアニメーションスピードはランダムです。

このデモでは、3つのスペキュラ付き平行光源処理をした、84ポリゴン、186頂点のモデルを500個描画したときの時間を測定しています。

itoxe-6

Xbox 360上では大体16,000個のオブジェクトを描画することができます。

ソースコードはDemo/GameAnimeフォルダの中にあり、シェーダーコードはDemo/Content/GameAnimeの中にあります。

他にも役に立ちそうなもの

4種類のデモの紹介をしましたが、このデモコードの中には他にも役立ちそうなものがあります。

  • TimeRuler (Demo/GameDebug/TimeRuler.cs)
  • Fpsカウンター (Demo/GameDebug/FpsCounter.cs)
  • デバッグコマンド (Demo/GameDebug/DebugCommandUI.cs)
  • Layout (Demo/Utils/Layout.cs)
  • StringBuilder拡張メソッド (Demo/Utils/StringBuilderExtensions.cs)

これらについては追々紹介していきます。

Comments (9)

  1. 注:今回紹介するコンポーネントは デバッグサンプル に入っています。 開発中にいろんなものを実行したい ゲーム開発中には様々な情報が欲しくなる場面が沢山あります。シンプルな情報であればブレークポイントを設定して変数を調べたりすることができますが、3Dゲームで画面に表示されている数十体もある敵のうちからひとつの敵の情報をデバッガを使って得るのは大変です。また、スタンドアローン実行している状態、例えばVisual

  2. 描画に関する不思議 XNAフレームワークを使って描画する場合、以下のような不思議な現象を経験することが少なからずあると思います。 DrawIndexedPrimitiveを呼び出すのに掛かった時間を測定したら、1,000ポリゴンのモデルと、1万ポリゴンのモデルで同じ時間だったんだけど、どうして?

  3. J-en says:

    いつも(内容についていけないので頭を抱えながら)拝見させていただいています。

     XNAの自分のゲームに、今回のサンプルの中にあった「シェーダーインスタンス」での描画方法を適用したいと思っています。

    各Matrixの参照情報を配列に保持し、HLSL側のinstanceIndex が配列番号となり、それを元にMatrixiを取得・算出しているということはわかったのですが、

    どこでこのHLSLの「instanceIndex:TEXCOORD1」に値をセットしているかがわかりません。

    応用が利きそうな技術のため、ぜひともご教授いただきたいと思います。よろしくお願いいたします。

  4. Yuichi Ito says:

    ShaderInstancePart.csの中のInitializeShaderInstancingの中で頂点宣言を追加して、同じソースの中のReplicateVertexDataメソッドの中で実際のインスタンス番号を頂点バッファへ追加しています。

  5. vFetchでスキンアニメーション、その1:vFetchってなに? Xbox 360のGPUはDirect X 9.0とDirect X 10の中間であると言われることがありますが、vFetchはその特徴を示すひとつの機能です。vFetchはシェーダー内で使えるアセンブリ命令で、Vertex

  6. vFetchでスキンアニメーション、その2:vFetchの使い方 Fetchを使ったスキンアニメーションの実装例を紹介する前にvfetchの基本的な使い方を紹介します。   例えば、以下のシェーダーコードのように頂点位置、色を使用する頂点シェーダーがあるとします。

  7. マテリアルバッチの中でDrawPrimitiveを読んでいる箇所がありますが、引数の3つ目はmeshPart.BaseVertexではなく、0ではないでしょうか?

    他のモデルを試してみたところ上手くいかず、0を入れると上手く表示されたので……

  8. Yuichi Ito says:

    報告ありがとうございました。修正したものに更新しておきました。