FPSカウンター

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

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コンポーネントを紹介します。