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 windowsSleep function
沒有留言:
張貼留言