ページ 11

FPSが60にならず58くらいを上下してしまう現象について

Posted: 2011年12月03日(土) 06:54
by かりんとう
FPSについて詰まってしまいました。

龍神録プログラミングの館、43 章の「きっちりFPSを制御してみよう」を参考にプログラムしていたのですが、
実行してみるとFPSの値が60にはならず、58.1位で止まってしまいました。

自分のプログラムのせいかと思い、43章のコードをコピペして試したのですが
値は60にならず、58.1位で止まる感じです。

以下実行したコード
main.cpp

コード:


#include "DxLib.h"

#define FLAME 60

//fpsのカウンタ、60フレームに1回基準となる時刻を記録する変数
int fps_count,count0t;
//平均を計算するため60回の1周時間を記録
int f[FLAME];
//平均fps
double ave;

//FLAME fps になるようにfpsを計算・制御
void fps_wait(){
    int term,i,gnt;
    static int t=0;
    if(fps_count==0){//60フレームの1回目なら
        if(t==0)//完全に最初ならまたない
            term=0;
        else//前回記録した時間を元に計算
            term=count0t+1000-GetNowCount();
    }
    else    //待つべき時間=現在あるべき時刻-現在の時刻
        term = (int)(count0t+fps_count*(1000.0/FLAME))-GetNowCount();

    if(term>0)//待つべき時間だけ待つ
        Sleep(term);

    gnt=GetNowCount();

    if(fps_count==0)//60フレームに1度基準を作る
        count0t=gnt;
    f[fps_count]=gnt-t;//1周した時間を記録
    t=gnt;
    //平均計算
    if(fps_count==FLAME-1){
        ave=0;
        for(i=0;i<FLAME;i++)
            ave+=f[i];
        ave/=FLAME;
    }
    fps_count = (++fps_count)%FLAME ;
}

//x,yの位置にfpsを表示
void draw_fps(int x, int y){
    if(ave!=0){
        DrawFormatString(x, y,GetColor(255,255,255),"[%.1f]",1000/ave);
    }
    return;
}



int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定


        // while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
        while( ProcessMessage()==0 && ClearDrawScreen()==0 ){

		// fps表示
		draw_fps(0,465);

		// FLAME fps になるように fps を計算・制御
		fps_wait();

		//裏画面を表画面に反映 
		ScreenFlip();

        }
        
        DxLib_End(); // DXライブラリ終了処理
        return 0;
} 



PC側に問題があるのかと思い、他のPCで試してみたのですが同じでした。
試しに「四聖龍神録Plus」を動かしてみると値は60で落ち着き、たまに59.~の値になりました。

自分のPC環境は、Windows 7 , Intel Core i3-2100 CPU @ 3.10 GHz
      メモリ(RAM) 4.00GB
      64ビット オペレーティングシステム
            デスクトップ画面 リフレッシュレート 60HZ
です。

タスクマネージャーでプログラム実行時のCPU使用率を見ても10%、
メモリも半分位しか使ってないようです。

過去ログをみると60にならない方の書き込みを見つけたので、
自分だけではないのかな?と思いつつもどうやっても60になってくれません。

youtubeに状態を録画した動画を上げました。


動画ではFPSの値が57くらいで上下していますが、58.1で止まることが多いように感じます。

なにとぞアドバイスよろしくお願いいたします。

Re: FPSが60にならず58くらいを上下してしまう現象について

Posted: 2011年12月03日(土) 09:44
by softya(ソフト屋)
ScreenFlip();がモニタの1/60周期の垂直同期にシンクロしてしまっているので、それをsleepによる同期がじゃましている可能性があります。
ためしにsleepをはずしてみてください。あと fps_wait()の位置も重要です。
※ インデント(字下げ)が間違っているので直しておきました。バグの原因となるので丁寧にインデントしてくださいね。

コード:

#include "DxLib.h"

#define FLAME 60

//fpsのカウンタ、60フレームに1回基準となる時刻を記録する変数
int fps_count,count0t;
//平均を計算するため60回の1周時間を記録
int f[FLAME];
//平均fps
double ave;

//FLAME fps になるようにfpsを計算・制御
void fps_wait(){
	int term,i,gnt;
	static int t=0;
	if(fps_count==0){//60フレームの1回目なら
		if(t==0)//完全に最初ならまたない
			term=0;
		else//前回記録した時間を元に計算
			term=count0t+1000-GetNowCount();
	}
	else    //待つべき時間=現在あるべき時刻-現在の時刻
		term = (int)(count0t+fps_count*(1000.0/FLAME))-GetNowCount();

	//    if(term>0)//待つべき時間だけ待つ
	//        Sleep(term);

	gnt=GetNowCount();

	if(fps_count==0)//60フレームに1度基準を作る
		count0t=gnt;
	f[fps_count]=gnt-t;//1周した時間を記録
	t=gnt;
	//平均計算
	if(fps_count==FLAME-1){
		ave=0;
		for(i=0;i<FLAME;i++)
			ave+=f[i];
		ave/=FLAME;
	}
	fps_count = (++fps_count)%FLAME ;
}

//x,yの位置にfpsを表示
void draw_fps(int x, int y){
	if(ave!=0){
		DrawFormatString(x, y,GetColor(255,255,255),"[%.1f]",1000/ave);
	}
	return;
}



int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定


	// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
	while( ProcessMessage()==0 && ClearDrawScreen()==0 ){

		// fps表示
		draw_fps(0,465);


		//裏画面を表画面に反映 
		ScreenFlip();

		// FLAME fps になるように fps を計算・制御
		fps_wait();
	}

	DxLib_End(); // DXライブラリ終了処理
	return 0;
}

Re: FPSが60にならず58くらいを上下してしまう現象について

Posted: 2011年12月03日(土) 15:24
by かりんとう
softya(ソフト屋)さん、アドバイスありがとうございます。
おっしゃるとおりsleep関数をはずしてみた所、FPS値が60~60.1になりました。
なにぶん初心者なので、理解力があまりないので分からないのですが、
とりあえずsleep関数がじゃまをしていた。という事がわかりました。
softya(ソフト屋) さんが書きました:ScreenFlip();がモニタの1/60周期の垂直同期にシンクロしてしまっているので、
これは、モニタのリフレッシュレートが60Hzの場合、ScreenFlip関数を使っていれば「処理速度を一定にする処理」は必要ないということでよいのでしょうか?
それと、モニタのリフレッシュレートが60Hzの場合、必ずこういう症状がおこるものなのでしょうか?

もし必ずおこるなら、モニタのリフレッシュレートを調べて60Hzの場合は、sleep関数を使わない(処理速度を一定にする処理をしない)
それ以外(60Hz以外)の場合、sleep関数を使う(処理速度を一定にする)処理をする
という感じにしなければいけないと思うのですが、
一般的にみなさんはこんな感じで処理をしているものなのでしょうか?
あと、モニタのリフレッシュレートって調べられるものなのでしょうか?

質問が多くて申し訳ありません。

インデント(字下げ)ですが全く気がつきませんでした。指摘と修正ありがとうございました。

Re: FPSが60にならず58くらいを上下してしまう現象について

Posted: 2011年12月03日(土) 18:58
by softya(ソフト屋)
関数名は忘れましたが、ScreenFlip();のシンクロを停止する関数があるはずです。停止すれば自前で同期する事は出来ます。
まあ、基本的にはScreenFlip();に任せたほうが綺麗に同期できると思います。

Re: FPSが60にならず58くらいを上下してしまう現象について

Posted: 2011年12月03日(土) 23:40
by かりんとう
ありがとうございました。
ScreenFlip();のシンクロを停止する関数を調べてみますね。
とりあえずFPSも安定したのでこれで解決とさせていただきます。

Re: FPSが60にならず58くらいを上下してしまう現象について

Posted: 2011年12月03日(土) 23:57
by ISLe
質問のコード動かしてみましたが、安定しないですね。
かりんとう さんが書きました:それと、モニタのリフレッシュレートが60Hzの場合、必ずこういう症状がおこるものなのでしょうか?
わたしの自作のフレームワークでは発生しませんね。
ブログで公開しているので良かったら参考にしてください。
かりんとう さんが書きました:もし必ずおこるなら、モニタのリフレッシュレートを調べて60Hzの場合は、sleep関数を使わない(処理速度を一定にする処理をしない)
それ以外(60Hz以外)の場合、sleep関数を使う(処理速度を一定にする)処理をする
という感じにしなければいけないと思うのですが、
一般的にみなさんはこんな感じで処理をしているものなのでしょうか?
あと、モニタのリフレッシュレートって調べられるものなのでしょうか?
そうしたほうがスマートですけど、自分が作るプログラムでは問題になってないので自前のウェイト処理をはさんでます。
コマ落ちをカウントしてFPSを自動調節するのに必要ですし。

モニタのリフレッシュレートはEnumDisplaySettingsで調べられます。
もしかしたら正しい値を返さないドライバがあるかもしれません。


ScreenFlipのシンクロを外すとテアリングが発生しますよ。
Aeroテーマでウィンドウモードだと発生しないので気が付かない場合があるかもしれません。

Re: FPSが60にならず58くらいを上下してしまう現象について

Posted: 2011年12月05日(月) 04:35
by かりんとう
返信が遅れてしまって申し訳ありません。
ISLe さんが書きました:わたしの自作のフレームワークでは発生しませんね。
60Hzなら必ず僕のような症状がでてしまうか分からなかったのと、
もしみんなそうならプログラムが大変だなあと思って悩んでいたので、とてもありがたい情報でしたありがとうございます。

さっそく、ISLeさんのブログからサンプルプログラムをダウンロードして、
58にしてあるFPS設定を60に変えて実行してみたところ、FPS値が正確に60.000~で安定していて感動しました。

初心者なので、こういうサンプルコードはありがたいです。
参考にさせていただきます。

他にも僕の知りたい情報がISLeさんのブログにあったのでうれしかったです。

アドバイスありがとうございました。