ホーム < ゲームつくろー! < クラス構築編 < FPS計測クラス : サンプルプログラム
FPS計測クラス : サンプルプログラム
クラス構築編「FPS計測クラス」で紹介したCFPSCounterクラスを公開します。以下のソースを「FPSConuter.h」及び「FPSCounter.cpp」ファイルに分けてコピペするか、もしくは以下のリンクよりヘッダーファイル及び実装ファイルをダウンロードしてプロジェクトに追加することで使用することが出来ます。
FPSCounter.lzh(1.85kb)
○ クラスの使い方
生成時にサンプル数を指定できます。生成後、描画ループのどこかで1度GetFPS関数を呼び出せば、その時のFPSの移動平均を算出してくれます。以下に、使用例を示します。
FPSCounter.h #include "FPSCounter.h" #include <fstream> using namespace std; int main() { Sleep(1000); const int c = 50; double F[c], R[c]; CFPSCounter FPS(10); // 生成してGetFPS関数を呼び出すだけです int i; // Sleep(100)の計測誤差の測定 for(i=0; i<c; i++){ Sleep(100); F[i] = 1.0 / FPS.GetFPS(); } // 空回しによる計測誤差の測定 for(i=0; i<c; i++){ R[i] = 1.0 / FPS.GetFPS(); } // 結果出力 ofstream ofs; ofs.open(" Res.txt"); for(i=0; i<c; i++){ ofs << F[i] << "\t" << R[i] << "\t" << F[i]-R[i] << endl; } return 0; }
これは、Sleep関数の精度を測定しています。最初にSleep(100)の間隔を測定し、次にGetFPS自体のオーバーヘッドによる誤差を無くすため、空回しの間隔のを測定しています。F[i]-R[i]でSleep(100)の精密測定値(移動平均)がres.txtファイルに出力されます。Sleep関数はあまり精度が高くないといわれていますが、これで測定しますと、その精度の「悪さ」が良くわかります。私の環境でReleaseモード実行時で0.100585くらいでした。本来は0.100000になってもらいたいわけですが、そうはいかないようです。ちなみに、オーバーヘッドは6.69749e-007くらいでした。
上の例は簡単のためにFPSを時間に戻しています。GetFPS関数の戻り値はFPS値です。
クラス
FPSCounter.h // FPS計測クラスヘッダーファイル #pragma once #pragma comment(lib, "winmm.lib") #include <windows.h> #include <mmsystem.h> #include <list> using namespace std; #define FPSCOUNTER_QUERYPER_COUNTER 1 #define FPSCOUNTER_TIMEGETTIME 2 class CFPSCounter { private: int m_iCounterFlag; // 使用する計測関数の判定フラグ LARGE_INTEGER m_Counter; // クロックカウント数 double m_dFreq; // 1秒当たりクロックカウント数(周波数) LONGLONG m_OldLongCount; // 以前のクロックカウント数 DWORD m_dwTGTOldCount; // 以前の時刻(ミリ秒) list<double> m_dwDefTimeLst; // 時間リスト UINT m_uiNum; // 移動平均計算時のデータ数 double m_dwSumTimes; // 共通部分の合計値 public: CFPSCounter(unsigned int smp = 10); public: virtual ~CFPSCounter(void); // FPS値を取得 double GetFPS(); // サンプル数を変更 void SetSampleNum( unsigned int smp); protected: // 現在の時刻を取得 double GetCurDefTime(); // FPSを更新 double UpdateFPS( double Def ); };
FPSCounter.cpp // FPS計測クラス実装ファイル #include "FPSCounter.h" // コンストラクタ CFPSCounter::CFPSCounter(unsigned int smp) { // サンプル数の設定 SetSampleNum(smp); // 計測する時計の選択 if(QueryPerformanceCounter( &m_Counter ) != 0) { // QueryPerformanceCounter関数を使うフラグ m_iCounterFlag = FPSCOUNTER_QUERYPER_COUNTER; m_OldLongCount = m_Counter.QuadPart; // 生成時の時刻(クロック数)を取得 LARGE_INTEGER Freq; QueryPerformanceFrequency( &Freq ); // 1秒当たりクロック数を取得 m_dFreq = (double)Freq.QuadPart; } else { // timeGetTime関数を使うフラグ m_iCounterFlag = FPSCOUNTER_TIMEGETTIME; // 精度を上げる timeBeginPeriod(1); // 生成時の時刻(ミリ秒)を取得 m_dwTGTOldCount = timeGetTime(); } // 計測 GetFPS(); } // デストラクタ CFPSCounter::~CFPSCounter(void) { if(m_iCounterFlag == FPSCOUNTER_TIMEGETTIME) timeEndPeriod(1); // タイマーの精度を戻す } // FPS値を取得 double CFPSCounter::GetFPS() { double Def = GetCurDefTime(); if(Def == 0){ // 計算できないのでを返す return 0; } return UpdateFPS( Def ); } // 現在の差分時刻をミリ秒単位で取得 double CFPSCounter::GetCurDefTime() { // 差分時間を計測 if(m_iCounterFlag == FPSCOUNTER_QUERYPER_COUNTER) { // QueryPerformanceCounter関数による計測 QueryPerformanceCounter( &m_Counter ); // 現在の時刻を取得し、 LONGLONG LongDef = m_Counter.QuadPart - m_OldLongCount; // 差分カウント数を算出する。 double dDef = (double)LongDef; // 倍精度浮動小数点に変換 m_OldLongCount = m_Counter.QuadPart; // 現在の時刻を保持 return dDef*1000 / m_dFreq; // 差分時間をミリ秒単位で返す } // timeGetTime関数による計測 DWORD CurTime = timeGetTime(); DWORD CurDef = CurTime - m_dwTGTOldCount; // 差分カウント数を算出する m_dwTGTOldCount = CurTime; // 現在の時刻を保持 return CurDef; } // FPSを更新 double CFPSCounter::UpdateFPS( double Def ) { // 最も古いデータを消去 m_dwDefTimeLst.pop_front(); // 新しいデータを追加 m_dwDefTimeLst.push_back( Def ); // FPS算出 double FPS; double AveDef = (m_dwSumTimes + Def) / m_uiNum; if(AveDef != 0) FPS = 1000.0 / AveDef; // 共通加算部分の更新 m_dwSumTimes += Def - *m_dwDefTimeLst.begin(); return FPS; } // サンプル数を変更 void CFPSCounter::SetSampleNum( unsigned int smp) { m_uiNum = smp; // 平均を計算する個数 m_dwDefTimeLst.resize(m_uiNum, 0.0); // リスト初期化 m_dwSumTimes = 0; }