XNA Frameworkブログ
 

December, 2008

  • ひにけにXNA

    タイムルーラー

    • 7 Comments

    注:今回紹介するコンポーネントはデバッグサンプルに入っています。

    TimeRuler

    時を測る

    前回紹介したFPSカウンターではゲーム全体のパフォーマンスを測定するには使えますが、どの処理がどれだけ時間が掛かっているかを判定するには不向きです。

    例えば、ねこが大勢の敵をなぎ倒す「ねこ無双」というゲームを作っていて、敵が沢山出てきた時に処理落ちになった場合に最適化をする必要があったとします。

    最適化ルールその3:「最も負荷の高い部分(ボトルネック)の処理を最適化する」

    これは当然の話で、1フレーム内で10%しか消費していない処理よりも、50%消費している部分を最適化する方が効果的です。では、この高負荷の処理を発見するにはどうしたら良いでしょうか?ねこ無双の場合だと、敵が増えると処理落ちがするんだから真っ先に思いつくのは敵AI、敵のアニメーション、敵のコリジョン判定、敵の描画処理部分です。

    しかし、必ずしもそうとは限りません。もしかしたら、敵の数ではなく戦っている場面の描画部分がもともと負荷が多かったのかもしれないし、敵を表示するレーダー部分かもしれません。

    このボトルネックを発見するのに有用なのがリアルタイムプロファイラー、TimeRuler(タイムルーラー)です。TimeRulerでは、複数の処理時間を測定することができ、その結果をグラフでリアルタイムに表示してくれます。

    上図の例では、青い部分が更新処理、黄色い部分が描画処理になっています。この状態で最適化する場合は更新処理部分を最適化するのが効果的だと言うことが一目で判ります。

    アルゴリズムのチェック

    理想的なアルゴリズムは計算量のオーダーがO(n)、もしくはO(n log n)になることです。例えば、敵を10体出現させたときの処理時間が1msだった場合、敵が100体になった時の処理時間が10ms~20ms以内に収まっていれば理想的なアルゴリズムを使っているということになります。逆に敵100体の処理時間が1,000msになってしまうのは、計算量オーダーがO(n^2)になっているアルゴリズムを使っている可能性があります。良くあるケースでは、コリジョン判定を単純な多重ループにしている時などです。

    特に最近のゲームでは大量のオブジェクト数を処理するので、アルゴリズムの選択を間違えると、あっという間に処理が間に合わなくなってしまうので注意が必要です。

    TimeRulerを使ってオブジェクトの数を10,20,...100と順々に増やしていき、それぞれの処理時間を測定してグラフにすることで問題のあるアルゴリズム部分を発見することができます。

    瞬間最大負荷を測る

    TimeRulerはリアルタイムプロファイラーなので、瞬間的な負荷も視覚的に見ることができます。例えば同じフレーム内で大量の敵を一気に発生させると、敵の初期化処理に時間が掛かりすぎて、そのフレームだけ処理落ちするということがあります。こういった瞬間的な負荷はFPSカウンターで発見することは不可能ですが、TimeRulerを使うとグラフが一瞬大きくブレるので判ります。

    また、TimeRulerを表示した状態でビデオに録画したり、動画をキャプチャーしておけば、こういった瞬間的な負荷を細かく調べることもできます。

    TimeRulerを使う為の準備

    TimeRulerコンポーネントはDebugManagerにあるフォントを使用するので、TimeRulerを追加する前にDebugManagerコンポーネントを追加する必要があります。

    // デバッグマネージャーの初期化と追加
    debugManager = new DebugManager( this );
    Components.Add( debugManager );

    また、必須ではありませんが、TimeRulerはインスタンス生成時にGame.ServicesにIDebugCommandHostインターフェースが追加されていると、trデバッグコマンドを登録します。DeubgCommandUIはIDebugCommandHostインターフェースを実装しているので、このコンポーネントを追加した後にTimeRulerを追加することでtrコマンドを使うこ���ができます。

    // デバッグマコマンドUIの初期化と追加
    debugCommandUI = new DebugCommandUI( this );
    
    // デバッグコマンドUIを最上面に表示させる為にDrawOrderを変更する
    debugCommandUI.DrawOrder = 100;
    
    Components.Add( debugCommandUI );

    デバッグコマンドUIをTabキーを押して表示させた後に、trとタイプするとTimerRulerの表示/非表示の切り替え、"tr on"で表示、"tr off"で非表示にすることができます。

    また、trコマンドには以下のオプションがあります。

    log ログの表示/非表示の切り替え。
    例) tr log
    reset ログ履歴の初期化
    例) tr reset
    frame TimeRulerのバーの長さをフレーム単位で指定する(既定値:1)
    例) tr frame:2

    プログラム側でも、これらの操作はできるようになっていて、表示の切り替えはTimeRuler.Visibleプロパティ、Logの表示はTimeRuler.ShowLogプロパティ、表示フレーム数はTimeRuler.TargetSampleFramesプロパティ、そしてログのリセットはTimeRuler.ResetLogメソッドを呼ぶことでできます。

     最後に、TimeRulerをComponentsに追加することでTimeRulerが画面下に表示されるようになります。ただし、DebugCommandUIが先に登録されていると、初期状態では表示されません。

    // タイムルーラーの初期化と追加
    timerRuler = new TimeRuler( this );
    Components.Add( timerRuler );

    TimeRulerの使い方

    上記の初期化を終えれば、TimeRulerを使う準備ができました。まず、TimeRulerにフレームの開始時点を知らせるStartFrameメソッドを呼びます。場所はGameクラスのUpdateメソッドの先頭に記述するのが良いでしょう。

    // タイムルーラーにフレーム開始を伝える
    timerRuler.StartFrame();

    次に測定したい部分をBeinMarkEndMarkメソッドで囲みます。BeginMarkメソッドには表示するバーのインデックス値(0~7)、マーカー名、バーに表示されるときの色を指定し、EndMarkではバーのインデックス値とマーカー名を指定します。マーカー名はCaseセンシティブなので、大文字小文字が違うと違うマーカー名と認識されることに注意してください。バーのインデックス値は省略するとインデックス値を0として呼び出した事になります。

    // 更新期間、"Update"の測定開始
    timerRuler.BeginMark( 0, "Update", Color.Blue );
    
    // Updateの処理をここでする
    ...
    
    // 更新期間、"Update"の測定終了
    timerRuler.EndMark( 0, "Update" );

    BeginMarkは以下のコードのように階層的に呼び出すこともできます。デフォルトでは最大32階層になっていますが、TimeRuler.MaxNestCall定数値を変更することができます。ただし、Begin A, Begin B, End B, End Aのように呼べますが、Begin A, Begin B, End A, End Bのように呼ぶことはできません。

    timerRuler.BeginMark( "Update", Color.Blue );
    
        timerRuler.BeginMark( "Character Update", Color.Azure );
    
            timerRuler.BeginMark( "Character Animation", Color.BurlyWood );
            // キャラクターアニメーション処理
            ...
            timerRuler.EndMark( "Character Animation" );
    
            timerRuler.BeginMark( "Character Collision", Color.DarkOrchid );
            // キャラクターコリジョン処理
            ...
            timerRuler.EndMark( "Character Collision" );
    
        timerRuler.EndMark( "Character Update" );
    
    timerRuler.EndMark( "Update" );

    TRACE定数でTimeRuler機能のコントロール

    TimeRulerはその性質上リリースビルドでも動作するようになっていますが、実際にゲームをリリースするときには実行する必要はありません。ゲームコード内に埋め込んだBegin/EndMarkを手で消したりするのはあまりにも不効率過ぎるので、簡単にそれらのメソッドの呼び出しを制御するためにTimeRulerの殆どのメソッドにはConditional属性を使ってTRACE定数が宣言されているときのみに呼び出されるようになっています。TRACE定数はプロジェクトプロパティのビルドタブにある「TRACE定数の定義」のチェックボックスによってコントロールすることができます。

    C/C++ではコンパイラ最適化の一つとして呼び出し先が空の関数の場合には関数呼び出しのコードを生成しないようになっていますが、.Netでは呼び出するメソッドが空のメソッドであっても、メソッド呼び出しコードを生成します。代わりにConditional属性を使い、指定された定数が宣言されていない場合は、そのメソッドに使われた引数も含めて呼び出しコードを生成しないようになっています。

    マシン性能のクセを知る

    ここではTimeRulerなんていう格好つけた名前をつけてはいますが、同様の仕組みは8ビット機時代から使われていたものです。例えばPCエンジンでは、あるパレットの色を変更するとTVの画面外の色を変更することができるので、処理毎に色を変えることで画面の両脇に縦に伸びるバーを作ることができました。

    実装方法は変わりましたが、バーが振り切らないようにゲームを作るという目的は今も昔も一緒です。特に、最近のシステムでは幾重ものパイプラインステージやキャッシュといった複雑な仕組みがあるので、必ずしもコード量が実行速度に比例するわけではありません。ですから、常にTimeRulerを使うことでどの処理がどれくらいの負荷になるのかを実感できることができます。

    次回はデバッグコマンドを紹介します。

  • ひにけにXNA

    FPSカウンター

    • 1 Comments

    注:今回紹介するコンポーネントはデバッグサンプルに入っています。

    fps-counter

    正確な測定には注意が必要

    FPSカウンターは、一定時間内に(数秒程度)何フレーム更新できたかを計測した結果から、1秒間のフレーム数、FPS(Frame Per Second)を表示するという、非常に簡単な機能です。

    ですが、正確なFPSを測定するには幾つかの注意が必要です。Game.IsFixedTimeStepの既定値はtrueになっているので、ゲームの更新と描画に掛かる時間が1msだとしても、見かけ上は16.6ms、つまり60FPSになってしまいます。また、描画時にV-Syncに同期するようになっているので、IsFixedTimeStepだけをfalseにしてもやはり60FPSになってしまいます。

    FPSを正しく測定するにはゲームクラスのコンストラクタ内で以下の様なコードを実行する必要があります。

    graphics = new GraphicsDeviceManager( this );
    
    graphics.SynchronizeWithVerticalRetrace = false;
    IsFixedTimeStep = false;

    これでFPS自体は正しく測定することができますが、IsFixedTimeStepをfalseに変更するということは、ゲームの更新の仕方が固定更新から、可変更新に変えるということです。もし、キャラクターを移動するコードが以下の様になっている場合に可変更新に変更すると、キャラクターの移動が速すぎてゲームがプレイ不可能になる場合があります。

    pos += speed;

    この問題を解決するには以下の様に、経過時間によって移動するようなコードになっている必要があります。

    float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
    
    pos += speed * dt;

    固定更新、可変更新の振る舞いの違いについては、この投稿が参考になると思います。もし、既に固定更新でゲームを作っている場合にはFPSカウンターを使う意味はあまりありません。

    GPUとCPUバランスに注意

    また、CPUとGPUの負荷バランスが違う場合には、FPS値は参考になりません。例えば60フレームのゲームを作っている時にFPS値が丁度60フレームになっているからといって、必ずしもこれ以上の処理を追加できないと言うわけではありません。例えば、1フレームあたりの処理時間が、CPUが50%、GPUが100%近く消費している場合、CPU側にはまだ50%の余力があるにも関わらずFPSの値は60になるので、余力が無いように見えてしまいます。逆にCPU100%、GPU50%の状態でも同じです。

    CPU、GPUバランスを調べる簡単な方法は、GPUの負荷を測定するにはUpdate内では何も処理をせずにDrawだけ実行し、CPUの負荷を測定するには逆にDrawの処理をしないようにする方法があります。ただし、Draw内にもCPU部分の処理があるので、もっと正確にCPUの負荷を測定するにはDrawPrimitiveのメソッドのみをコメントアウトする必要があります。

    60FPSにならないんだけど

    IsFixedTimeStepとSynchronizeWithVerticalRetraceをtrueにした状態で、明らかにゲーム全体の処理が16.6ms以内で済んでいるのにも関わらず、FPSにはいつも59.xxしか表示されない。というのはおかしいと思う人もいるかもしれません。

    これには3つの理由があります。ひとつめはTVの60フレームというのは実際には59.94(厳密に言えば60/ 100.1%)になるので、理論的に60と言う数値にはならないこと。ふたつめは、測定する場合に1フレーム以下の余りの時間分で誤差がでるということ。そして、ハードウェア的には59.94でフレームを更新していても、ソフトウェア的に測定ポイントまで戻ってくるまでの時間が微妙に変わってくるということです。

    ですから、59.xxと言う数値を見たときには約60FPSと考えても問題ありません。

    FpsCounterコンポーネントの使い方

    FpsCounterコンポーネントはDebugManagerにあるフォントを使用するので、FpsCounterを追加する前にDebugManagerコンポーネントを追加する必要があります。

    // デバッグマネージャーの初期化と追加
    debugManager = new DebugManager( this );
    Components.Add( debugManager );

    また、必須ではありませんが、FpsCounterはインスタンス生成時にGame.ServicesにIDebugCommandHostインターフェースが追加されていると、fpsデバッグコマンドを登録します。DeubgCommandUIはIDebugCommandHostインターフェースを実装しているので、このコンポーネントを追加した後にFpsCounterを追加することでfpsコマンドを使うことができます。

    デバッグコマンドUIをTabキーを押して表示させた後に、fpsとタイプするとFPSの表示/非表示の切り替え、"fps on"で表示、"fps off"で非表示にすることができます。

    // デバッグマコマンドUIの初期化と追加
    debugCommandUI = new DebugCommandUI( this );
    
    // デバッグコマンドUIを最上面に表示させる為にDrawOrderを変更する
    debugCommandUI.DrawOrder = 100;
    
    Components.Add( debugCommandUI );

    最後に、FpsCounterをComponentsに追加することでFPSが画面左上に表示されるようになります。ただし、DebugCommandUIが先に登録されていると、初期状態では表示されません。

    // FPSカウンターの初期化と追加
    fpsCounter = new FpsCounter( this );
    Components.Add( fpsCounter );

    FpsCounterはDrawableGameComponentから派生しているので、プログラム上から表示/非表示をコントロールするにはVisibleプロパティを変更することでできます。

    fpsCounter.Visible = true

    次回はリアルタイムプロファイラーであるTimerRulerコンポーネントを紹介します。

  • ひにけにXNA

    デバッグサンプル

    • 7 Comments

    2010/09/17 追記: XNA Game Studio 4.0用のサンプルをhttp://higeneko.net/hinikeni/sample/xna40/DebugSample.zipにアップしました。詳細は「サンプルコードをXNA 4.0向けに更新」を見てください。

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

    2009年1月24日追記: サンプルプログラムのキーボード処理部分でテンキーの0を押すと1が入力されるバグを修正、アップデートしました。バグがあった場所はGameDebug/KeyboardUtils.csファイル内のInitializeKeyMapメソッド内の初期化コード部分です。AddKeyMap( Keys.NumPad0, "10" )となっているのをAddKeyMap( Keys.NumPad0, "0" )にする事で修正できます。連絡してくださったDERNAさんに感謝します。

     

    デバッグ3種の神器

    ScreenShot

    ゲーム開発現場で最も使われているゲーム内で動作するデバッグ用の機能といえば以下の3つがあります。

    • FPSカウンター
    • リアルタイムプロファイラー
    • デバッグコマンド

    市販されているPCゲームの中にはオプション画面でFPSカウンターを表示できるようになっているものもあるので、パフォーマンスの指標として有名です。後のふたつについては一般にはあまり知られていませんが、ゲーム開発現場、特に北米のゲーム開発現では良く目にする機能です。

    前回紹介したGamefest Japan 2008のデモでも、これらの機能を使っています。そこで、今回は前回のデモからデバッグ機能だけを抜き取ったサンプルを作りました。

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

    前回と同じく、フォントはConsolasを使用しています。Consolasフォントが無い場合はビルドエラーになるので、以下のURLからConsolasフォントをダウンロードするか、フォント名を変更するようにしてください。

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

    使い方

    サンプルを起動すると、画面右上に使い方が表示されます。

    • FPSカウンターの表示
      • Aボタン、またはAキー
    • TimeRulerの表示
      • Bボタン、またはBキー
    • TimeRulerログ表示
      • Xボタン、またはXキー
    • デバッグコマンドUIの表示
      • Tabキー
    • サンプルプログラムの終了
      • Backボタン、またはEscapeキー

    デバッグコマンドUIはキーボード入力からデバッグ用コマンドを実行します。Xbox 360でもUSBキーボードを本体に繋げることで使うことができます。デバッグコマンドUIには元々使えるコマンドとして画面をクリアするclsコマンド、登録されているコマンドを表示するhelpコマンドがあります。基本的にWindowsにあるコマンドシェルと使い方は一緒で、上下キーで最近使ったコマンドのヒストリを表示する機能もあります。

    このデモでは、DebugSampleGame.cs内には独自のデバッグコマンドを追加するサンプルコードが含まれていて、ここではposコマンドを追加して、画面上に表示される「+」マークの位置を表示したり、変更できるようになっています。下の動画では見づらいですが、最初は左上にあった白い点がposコマンドによって画面下中央に移動しているのが判ると思います。

    サンプルに含まれるファイル

    このサンプルに含まれるファイルには、デバッグ機能の使い方のサンプルコードが入ったDebugSampleGame.csファイルの他に、デバッグ機能を提供する以下の8つのファイルがあります。

    DebugSample/GameDebug/DebugCommandUI.cs デバッグコマンドUIクラス
    DebugSample/GameDebug/DebugManager.cs デバッグ用のコンテントを格納する為のゲームコンポーネント
    DebugSample/GameDebug/FpsCounter.cs FPSカウンター
    DebugSample/GameDebug/IDebugCommandHost.cs デバッグコマンド用のインターフェース
    DebugSample/GameDebug/KeyboardUtils.cs キーボード入力用ユーティリティクラス
    DebugSample/GameDebug/TimeRuler.cs タイムルーラー
    DebugSample/Utils/Layout.cs 画面レイアウト用構造体
    DebugSample/Utils/StringBuilderExtensions.cs StringBuilder用拡張メソッド宣言クラス

    自分のゲームにデバッグ機能を追加するには、上記の8つのファイルをプロジェクトに追加し、DebugManager、FpsCounter、TimeRuler、そしてDebugCommandUIコンポーネントをゲームのInitializeメソッド内で以下の様に追加する事で使用することができます。

    // デバッグマネージャーの初期化と追加
    debugManager = new DebugManager( this );
    Components.Add( debugManager );
    
    // デバッグマコマンドUIの初期化と追加
    debugCommandUI = new DebugCommandUI( this );
    
    // デバッグコマンドUIを最上面に表示させる為にDrawOrderを変更する
    debugCommandUI.DrawOrder = 100;
    
    Components.Add( debugCommandUI );
    
    // FPSカウンターの初期化と追加
    fpsCounter = new FpsCounter( this );
    Components.Add( fpsCounter );
    
    // タイムルーラーの初期化と追加
    timerRuler = new TimeRuler( this );
    Components.Add( timerRuler );

    DebugManagerとDebugCommandUIは自分自身をサービスとして登録するので、この二つは他のデバッグ用コンポーネントの前に追加する必要があります。

    次回から、それぞれのデバッグコンポーネントの使い方に付いて説明していきます。

  • ひにけにXNA

    Gamefest Japan 2008 デモプログラム

    • 9 Comments

    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)

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

Page 1 of 1 (4 items)