ホーム < ゲームつくろー! < クラス構築編 < 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;
}