正確な待つの実装

23 views
Skip to first unread message

Tatumakigen

unread,
Mar 18, 2011, 10:25:04 AM3/18/11
to なでしこ ゲーム開発研究所
【議題】 正確な時間待機させるにはどうすればいいか?
【投稿者】 Tatumakigen
【目的】 無限ループ内での安定したFPSを確保するために正確な待機時間を持たせる。
【目標】 正確無比に指定したFPSで描画できるようにする。
【問題点】 標準命令の” 待つ ”が7msec以下を扱えない。WindowsAPIの” Sleep(int value) ”が正しく動作しな
い。
【アプローチ】 (~)間 は処理が持っていかれてしまうので使えない。全く別の方法で正確に待機させる必要がある。
WindowsAPIの SuspendThread() と ResumeThread() 関数を用いてできないか?
参考URL:http://msdn.microsoft.com/ja-jp/library/cc429364.aspx
関数の問題点:ハンドルからスレッドを停止させると復帰できない。

【参考】
●FPS固定({値渡し}NUM)
  もし、FRAME_COUNTER = 60 ならば、
    FRAME_SUM = 0 ;
    60回
      FRAME_SUM = FRAME_SUM + FRAME_AVE[回数] ;
    ここまで
    FRAME_SUM = FRAME_SUM / 60 ;
    FRAME_SUM = 1000 / FRAME_SUM
    FRAME_COUNTER = 0 ;
  SPENDTIME = システム時間 - SYSTEMTIME
  REQTIME = ( NUM - SPENDTIME )
  もし、SPENDTIME < NUMならば
    //Sleep(REQTIME)
    ((REQTIME)/1000)秒待つ
  FRAME_AVE[FRAME_COUNTER] = システム時間 - SYSTEMTIME //ループバックしてくるまでにかかった時間
  SYSTEMTIME = システム時間 ;
  FRAME_COUNTER = FRAME_COUNTER + 1 ;


//WindowsAPIによるSleep関数
●Sleep(value)=DLL("kernel32.dll",
"VOID Sleep(
DWORD dwMilliseconds
)")

#注意:変数は全てグローバル扱いです。



この問題に関する情報を募集します。
間違い等ありましたらご指摘下さい。

Tatumakigen

unread,
Mar 28, 2011, 11:06:03 AM3/28/11
to なでしこ ゲーム開発研究所
FPS固定用の関数を少し修正しました。

WinAPIによるtimeGetTime関数の精度はなでしこのシステム時間と一致しました。
恐らく内部では同じ関数を呼び出していますから、差は500μ秒程度だと思います。

【問題点】
Sleep関数では描画スレッドが停止されるという重大な問題点を抱えている。
【改善余地】
メインループ内で処理されたとされる描画命令は命令実行後数ミリ秒後に母艦、あるいは指定したDCに描画されるため、この数ミリ秒を把握すること。
待つの精度は10msecで25%程であり、Sleepと併用しても安定しない。
待つ以外の処理を挟むことで描画スレッドの終了を待ったあと終了後その場で経過時間を計測し、Sleep関数に渡すというのが現実的。
ただしこれは速度『 規制 』の手段であって、仮に描画スレッドが予定時刻内に終わらない場合はフレームスキップかもしくは別の方法で対処する必要があ
る。
この構造では処理速度が環境依存する可能性が強まり、結局のところFPSを固定するという事はできない。

【要求】
効率的な空の待機時間を設定する方法を考案して欲しい。
ただし、それは環境に依存することはなく、誤差5%以内であること。
必要であれば事前にマシンの性能評価を行う形でのループを構成し、本体が自動で組み込む形での実装でも良い。
ただし所要時間はなるべく短く、かつその後の動作に支障を来さないこと。

【ソース・参考】
●FPS固定({値渡し}NUM)
  もし、(FRAME_COUNTER==NUM)ならば、
    FRAME_SUM=0;
    (NUM)回
      FRAME_SUM=FRAME_SUM+FRAME_AVE[回数];
    ここまで
    FRAME_SUM=FRAME_SUM/NUM;
    FRAME_SUM=1000/FRAME_SUM
    FRAME_COUNTER=0;
  SPENDTIME=システム時間-SYSTEMTIME//掛けた時間
  REQTIME=((1000/NUM)-SPENDTIME)
  もし、(SPENDTIME<(1000/NUM))ならば、
    ((REQTIME-1)/1000)秒待つ
  FRAME_AVE[FRAME_COUNTER]=timeGetTime-SYSTEMTIME //ループバックしてくるまでにかかった時

  SYSTEMTIME=timeGetTime;
  FRAME_COUNTER=FRAME_COUNTER+1;

●Sleep(p1)=DLL("kernel32.dll","VOID Sleep(DWORD dwMilliseconds)")

●timeGetTime() =DLL("WINMM.dll","DWORD timeGetTime(VOID)")

Tatumakigen

unread,
Mar 31, 2011, 2:06:36 PM3/31/11
to なでしこ ゲーム開発研究所
現在この問題の解決度は80%です。

【ソース】

!変数宣言は必要

WAITTIMEとは整数=0;
GET_SYSTEMTIMEとは整数=0;
FRAME_COUNTERとは整数=0;
FRAME_AVEとは配列=0;
FRAME_SUMとは実数=0;
SYSTEMTIMEとは整数=0 ;
SPENDTIMEとは整数=0;
VAL_WAITTIMEとは整数=0;

●FPS固定({値渡し}NUM)
  もし、(FRAME_COUNTER==NUM)ならば、
    FRAME_SUM=0;
    (NUM)回
      FRAME_SUM=FRAME_SUM+FRAME_AVE[回数];
    ここまで
    FRAME_SUM=FRAME_SUM/NUM;
    FRAME_SUM=1000/FRAME_SUM
    FRAME_COUNTER=0;
  SPENDTIME=システム時間-SYSTEMTIME//掛けた時間
  もし、(SPENDTIME<(1000/NUM))ならば、
    (0)秒待つ
    ((SYSTEMTIME+1000/NUM)>=(システム時間))間
      待機時間
    ここまで
  FRAME_AVE[FRAME_COUNTER]=timeGetTime()-SYSTEMTIME //ループバックしてくるまでにかかった
時間
  SYSTEMTIME=timeGetTime();
  FRAME_COUNTER=FRAME_COUNTER+1;

●timeGetTime() =DLL("WINMM.dll","DWORD timeGetTime(VOID)")

●待機時間
  50回
    VAL_WAITTIME=VAL_WAITTIME+1
  VAL_WAITTIME=0

#ここまで



timeGetTime()はなでしこのシステム時間と全く同じ精度です。
待機時間という関数ではただひたすら無駄な足し算をして時間を潰します。

ところが、これには大きな問題があります。
足し算でも引き算でもどちらでもいいのですが、この計算には非常にマシンのスペックの差が出てきます。
CPUのクロック数とも強い関係があるため、ネットブックの様なライト向けのPCでは全く動作しません。
基本的に100回を1msec程度に扱いたいと思っていたのですが、ネットブックで一回の計算の評価をしたところ5msec程掛かっていました。
これは何かの間違いだとは思いますが、使用するマシンのスペックによって全く違う結果が得られるという重大な欠陥を抱えています。
しかも浮動小数点演算を含まない四則演算でこの速度の違いは頭を抱えざるを得ません。


また付随して描画速度や、描画スレッドの遅延による描画漏らし等ゲームを作る上で深刻な悩みを数多く抱えています。
このような問題点に関するアプローチが散見されないのは、過去に先人がそうした問題を丸投げしてきたからです。
あのJavaも最初は遅すぎると言われていたくらいですから、なでしこも然りです。

Tatumakiは速度向上、効率化構想を磨き上げなでしこでもゲームプログラミングができるということを証明します。
それにすこしばかりご協力ください。


尚、上記のFPS固定関数では必ず待機時間を500μsec~1msecに予め収まるように調整してからお使いください。
Reply all
Reply to author
Forward
0 new messages