2018年5月14日 星期一

Win32的Gameloop的滑順問題(1)

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

沒有留言:

張貼留言