FPS制御で指定したFPS+1になる

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

FPS制御で指定したFPS+1になる

#1

投稿記事 by みけCAT » 10年前

Windows Vista Home Premium SP2 32ビット
Intel(R) Core(TM)2Duo T8100 @2.10GHz 2.10GHz
RAM 4.00GB
GCC(MinGW) 4.8.1

usleep関数を用いたFPS制御のプログラムを書いたのですが、なぜか指定したFPS+1に近い値が測定されます。
1FPSや10FPSに設定しても+1になったので、誤差ではないと思います。
FPS測定(calcFPS関数)とFPS制御(adjustFPS関数)のどっちにバグがあるかもわからないのですが、
どうしてFPSが1多くなってしまうのでしょうか?
よろしくお願いします。

このルーチンはOpenGLを用いたプログラムに組み込んでいるのですが、
OpenGLを使わないプログラムでも再現したので、それを載せます。

fat.cpp

コード:

#include <sys/time.h>
#include <unistd.h>
#include <cstdio>

static int fpslimit=60;

long long getNowTimeUs(void) {
	timeval tv={0,0};
	gettimeofday(&tv,NULL);
	return (long long)(tv.tv_sec*1000000+tv.tv_usec);
}

double calcFPS(bool& fpsUpdated) {
	static double fps=0;
	static long long prevTime=getNowTimeUs();
	static int frameCount=0;
	long long nowTime=getNowTimeUs();
	frameCount++;
	if(prevTime+1000000<=nowTime) {
		fps=frameCount*1000000.0/(nowTime-prevTime);
		prevTime=nowTime;
		frameCount=0;
		fpsUpdated=true;
	} else {
		fpsUpdated=false;
	}
	return fps;
}

void adjustFPS(void) {
	static long long prevTime=getNowTimeUs();
	static int frameCount=0;
	long long nowTime;
	long long timeDiff;
	if(fpslimit<=0)return;
	nowTime=getNowTimeUs();
	frameCount++;
	timeDiff=1000000*frameCount/fpslimit-(nowTime-prevTime);
	if(timeDiff>0)usleep(timeDiff);
	if(frameCount>=fpslimit) {
		prevTime=nowTime;
		frameCount=0;
	}
}

int main(int argc,char* argv[]) {
	if(argc>=2)sscanf(argv[1],"%d",&fpslimit);
	printf("fpslimit=%d\n",fpslimit);
	for(;;) {
		double fps;
		bool fpsUpdated=false;
		fps=calcFPS(fpsUpdated);
		if(fpsUpdated) {
			printf("%.2ffps\n",fps);
		}
		adjustFPS();
		for(volatile int i=0;i<100000;i++); // 適当な負荷をかける
	}
	return 0;
}
Makefile

コード:

fat.exe: fat.cpp
	g++ -O2 -s -o fat.exe fat.cpp
オフトピック
ここでのfatは「FPS Adjust Test」の略です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
usao
記事: 1889
登録日時: 11年前

Re: FPS制御で指定したFPS+1になる

#2

投稿記事 by usao » 10年前

おそらく30億光年くらい的外れかと思いますが,38行目の
>timeDiff=1000000*frameCount/fpslimit-(nowTime-prevTime);
の除算のあたりって大丈夫なのでしょうか

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: FPS制御で指定したFPS+1になる

#3

投稿記事 by みけCAT » 10年前

回答ありがとうございます。
38行目の下に

コード:

	printf("%d/%d frame now %lld prev %lld\ndiff %lld target %d timeDiff %lld\n",
		frameCount,fpslimit,nowTime,prevTime,
		nowTime-prevTime,1000000*frameCount/fpslimit,timeDiff);
という処理を入れると、timeDiffが不自然に0(に近い値)になっている時があることがわかりました。
原因は調査中です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: FPS制御で指定したFPS+1になる

#4

投稿記事 by みけCAT » 10年前

fpslimitフレームの組の最初のフレームの処理時間に直前のusleepによって停止した時間を含めてしまっていたので、
adjustFPS関数を以下のようにしたところ、満足のいく結果が得られました。

コード:

void adjustFPS(void) {
	static long long prevTime=getNowTimeUs();
	static int frameCount=0;
	long long nowTime;
	long long timeDiff;
	if(fpslimit<=0)return;
	nowTime=getNowTimeUs();
	frameCount++;
	timeDiff=1000000*frameCount/fpslimit-(nowTime-prevTime);
#if 0
	printf("%d/%d frame now %lld prev %lld\ndiff %lld target %d timeDiff %lld\n",
		frameCount,fpslimit,nowTime,prevTime,
		nowTime-prevTime,1000000*frameCount/fpslimit,timeDiff);
#endif
	if(timeDiff>0)usleep(timeDiff);
	if(frameCount>=fpslimit) {
		prevTime=getNowTimeUs();
		frameCount=0;
	}
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: FPS制御で指定したFPS+1になる

#5

投稿記事 by みけCAT » 10年前

もう少しだけ修正しました。
なお、最初の1秒だけFPSが多いのは仕様とします。

コード:

void adjustFPS(void) {
	static long long prevTime=getNowTimeUs();
	static int frameCount=0;
	long long nowTime;
	long long timeDiff;
	if(fpslimit<=0)return;
	nowTime=getNowTimeUs();
	frameCount++;
	timeDiff=1000000*frameCount/fpslimit-(nowTime-prevTime);
	if(timeDiff>0)usleep(timeDiff);
	if(frameCount>=fpslimit || nowTime-prevTime>1000000) {
		prevTime=getNowTimeUs();
		frameCount=0;
	}
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: FPS制御で指定したFPS+1になる

#6

投稿記事 by ISLe » 10年前

わたしが使っているのはこんなコードです。
#ブログで公開しているものをアレンジしました。

メリットは、フレーム数単位で最大遅延をコントロールできることと、何フレームすっ飛ばしたか戻り値で分かることです。
描画をスキップして動的にフレームレートを変化させたりする際のヒントにできます。

コード:

int adjustFPS(void) {
	static long long prevTime=getNowTimeUs();
	static int frameCount=0;
	long long nowTime;
	long long timeDiff;
	if(fpslimit<=0)return 0;
	nowTime=getNowTimeUs();
	frameCount++;
	int delayCount=0;
	for (;;) {
		timeDiff=(prevTime+1000000*frameCount/fpslimit)-nowTime;
		if(frameCount>=fpslimit) {
			prevTime+=1000000;
			frameCount=0;
		}
		if(timeDiff>0) {
			usleep(timeDiff);
			break;
		}
		if(++delayCount>=8) { // 8フレーム以上遅延したら
			// リセット
			prevTime=getNowTimeUs();
			frameCount=0;
			delayCount=0;
			break;
		}
	}
	return delayCount;
}

閉鎖

“C言語何でも質問掲示板” へ戻る