トップページ > 記事閲覧
垂直同期信号待ちしないでfpsを制限したい
名前:すっぴー 日時: 2014/03/18 18:53

DxライブラリのScreenFlipは、実行した際に内部で垂直同期信号を待つことで、 fpsをモニターのリフレッシュレートに合わせ、ティアリングを防止していると解釈しています。 今回、キーボードの入力を10ms以下の精度で取る必要が出たため、SetWaitVSyncFlagをfalseにしました。 その際、ティアリングの発生を確認したので、ScreenFlipを呼ぶタイミングを60t/sに制限することで、ティアリングを抑制しようと考えました。 そのためのScreenFlip代理実行クラスを作成したのですが、実行時にScreenFlipが30t/s程度しか呼ばれていないようなのです。 ScreenFlipを呼ぶタイミングで、NowCount-mLastCountを出力すると、5回周期で16ms,16ms,16ms,37ms,50msといった具合になります。 メインのゲーム処理自体は1msもかかっていないため、通常の処理落ちとは異なる挙動であることは確かなようです。また、fps.wait()をはさむと正常に60fps台で動作することを確認しました。 このWaitを実行すると正常に動作するというのが自分の中では不可解極まりなく、原因もわからないため頭を抱えている状況です。お知恵を貸していただけないでしょうか。
メンテ

Page: 1 |

Re: 垂直同期信号待ちしないでfpsを制限したい ( No.1 )
名前:すっぴー 日時:2014/03/18 18:54

以下が実装です class Fps{ int mStartTime; //測定開始時刻 int mVSTime; int mfpsCount; //カウンタ double mFps; //fps static const int N; //平均を取る時間(ms) int FPS; //設定したFPS public: int Update(); int Draw(); int Wait(); double Getfps(); void Setfps(int _fps); Fps(void); }; const int Fps::N = 500; Fps::Fps(void) { mStartTime = 0; mVSTime = 0; mfpsCount = 0; mFps = 0; FPS = 60; } int Fps::Update(){ ++mfpsCount; //カウンタを加算 mVSTime = GetNowCount(); if(mVSTime - mStartTime > N){ mFps = mfpsCount / double((mVSTime - mStartTime) * 1.0 / 1000); mfpsCount = 0; mStartTime = mVSTime; } return 0; } int Fps::Draw(){ DrawFormatString(0, 0, GetColor(255,255,255), "%.1ffps", mFps); return 0; } int Fps::Wait(){ int tookTime = GetNowCount() - mVSTime; //かかった時間 int waitTime = 1000/FPS - tookTime; //待つべき時間 if( waitTime > 0 ) Sleep(waitTime); //待機 return 0; } double Fps::Getfps(){ return mFps; } void Fps::Setfps(int _fps){ FPS = _fps; } class FlipScreenProxy{ private: LONGLONG mLastCount; LONGLONG mRateInterval; Fps fps; public: FlipScreenProxy(int rate = 60){ mRateInterval = (1000000 / rate); mLastCount = GetNowHiPerformanceCount(); } void SetRefreshRate(int rate){ mRateInterval = (1000000 / rate); mLastCount = GetNowHiPerformanceCount(); } int ScreenFlip(){ LONGLONG NowCount = GetNowHiPerformanceCount(); fps.Draw(); DrawFormatString(0,100,GetColor(255,0,0),"%d",NowCount - mLastCount); if (NowCount - mLastCount > mRateInterval){ //if (true){ fps.Update(); DrawFormatString(0, 200, GetColor(0, 255, 0), "%d", NowCount - mLastCount); mLastCount = GetNowHiPerformanceCount(); DxLib::ScreenFlip(); } //fps.Wait(); return 0; } };
メンテ
Re: 垂直同期信号待ちしないでfpsを制限したい ( No.2 )
名前:すっぴー 日時:2014/03/18 18:56

追記 ゲームのループ処理はすべてmsベースで行っているため、fps変動によるゲーム速度の変動はありません。
メンテ
Re: 垂直同期信号待ちしないでfpsを制限したい ( No.3 )
名前:softya(ソフト屋) 日時:2014/03/18 20:48

メインループは垂直同期のままで、スレッドで入力を管理したほうが安定的に良いかと思います。 ところで一般的なキーボードで10msの周期でちゃんと入力取れますでしょうか? ゲーマー向きキーボードならともかくチャタリングを考えると難しい気がするのです。実際にPCで確かめたことがないので誤情報かもしれませんので参考程度にお願いします。
メンテ
Re: 垂直同期信号待ちしないでfpsを制限したい ( No.4 )
名前:すっぴー 日時:2014/03/20 08:01

メインループとは別のスレッドで入力系統を管理するループを作成し、非同期的に処理を行う、という解釈でよいのでしょうか…? それですとやはり16msごとにしか入力を処理できないため、どうしても精度が落ちてしまうように感じるのですが… キーボードが高精度の入力を行えるか、ということに関しましては、 初期にフレームベースで作成した時よりも明らかに高精度になっていましたので、問題ないかと思われます。
メンテ
Re: 垂直同期信号待ちしないでfpsを制限したい ( No.5 )
名前:softya(ソフト屋) 日時:2014/03/20 10:34

とりあえず十分に速度が出るのなら、16.66msの半分=120fpsで描画以外の処理するのはどうでしょうか? そうすれば難しい制御せずに2回に1回描画するだけで済みそうです。
メンテ
Re: 垂直同期信号待ちしないでfpsを制限したい ( No.6 )
名前:管理人 日時:2014/03/21 22:22

私の環境では fps.Wait() を有効にしなくても 30fps になることはありませんでした 推測ですが載せて頂いたクラスを使ったゲームループを行った場合メインスレッドは休むことなく 走り続けるので、CPUのコア数に余裕が無いときは Direct3D のスレッドの時間まで奪ってしまい、 ScreenFlip 内部で呼んでいる Direct3D の処理が間に合わず、4回目の ScreenFlip 時に 『まだ前回の ScreenFlip の処理が終わってないので待っててね』と、37ms, 50ms またさせる ことになってしまっているのかもしれません、それが Wait 関数を呼ぶことで Sleep が実行され、 Direct3D のスレッドに処理をする余裕が生まれて症状が改善されたのではないかと思います
メンテ
Re: 垂直同期信号待ちしないでfpsを制限したい ( No.7 )
名前:すっぴー(解決) 日時:2014/03/22 20:00

早すぎる処理も考え物、ということですか… 私の環境でもSleep(2)程度でfpsを60にすることができました。このあたりの数値を外部ファイルから読み込むことで何とかしようと思います。 ユーザー任せ、というのは欠陥でしかないですが… ソフト屋様、管理人様、ありがとうございました。
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存