Win32的Gameloop的滑順問題(1)
前言
最近發現win32寫出來的Gameloop有不穩定的現象,像是FPS有偶而卡頓、FPS不滑順等問題,這裡把研究過程記錄下來。
內容
一般的Gameloop如以下程式碼
while( true ) {
if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
if ( WM_QUIT == msg.message ) {
break;
} else {
TranslateMessage( &msg);
DispatchMessage( &msg);
}
} else {
gameloop();
}
}
在我利用Timer測執行時間後發現PeekMessage()的部分(含TranslateMessage() ),有的時候執行時間高達20mSecs,比60FPS(16.67mSecs)還要長,而在
Game loop on windows裡,找到一個回答是用下列程式碼
while( !finished ) {
DWORD currentTick = GetTickCount();
DWORD endTick = currentTick + 1000/FRAMES_PER_SECOND;
while (currentTick < endTick) {
if ( PeekMessage( &m_msg, m_hWnd, 0, 0, PM_REMOVE ) ) {
if ( WM_QUIT == m_msg.message ) {
finished = true;
break;
} else {
TranslateMessage( &m_msg );
DispatchMessage( &m_msg );
}
currentTick = GetTickCount();
} else {
break;
}
}
processFrame();
}
簡單的說他給PeekMessage()的部分一個執行的時間,如果超過就直接執行gameloop,這個方法我實際實驗是沒效果的,那個20mSecs似乎不是多個MSG造成的,而是單一一個MSG就有這樣的時間!這個目前沒找到解決的方案。
本來以為FPS不滑順也是上述的問題造成的,但後來發現不是,以下是gameloop的程式碼
//do something
//...
double updateTime=m_cTimer.Elapsed();
double frameTime=(1.0f/60.0f);
if(updateTime < frameTime )
{
double delTime = frameTime - updateTime;
::Sleep( (size_t)(delTime*1000.0f) );
}
m_cTimer.Reset();
這樣的gameloop會非常不滑順!發現主要的問題是Sleep(),睡眠的時間並不是相當的準,解決的方法是將多的睡眠時間給下一個frame,並且過低的睡眠時間直接放棄,完成的程式碼如下
//do something
//...
double updateTime=m_cTimer.Elapsed()+m_flFPSModefiyTime;
double frameTime=(1.0f/60.0f);
if(updateTime < frameTime )
{
double delTime = frameTime - updateTime;
if(delTime > 0.001f)
::Sleep( (size_t)(delTime*1000.0f) );
m_flFPSModefiyTime=m_cTimer.Elapsed()+m_flFPSModefiyTime-frameTime;
//Set max m_flFPSModefiyTime was half frame time!
m_flFPSModefiyTime=fmin(frameTime*0.5,m_flFPSModefiyTime);
}
else
{
//Frame not on time!
m_flFPSModefiyTime=0.0f;
}
m_cTimer.Reset();
這樣出來的FPS就相當滑順。
後記
這次還有問題沒解決,就是20mSecs得MSG的問題,以後有機會再來解決!
參考資料
Game loop on windows
Sleep function