2017-09-16 57 views
2

我用std :: chrono C++庫編寫了下面的代碼,我試圖做的是 修復應用程序的FPS 60,但我得到50 FPS,肯定不是性能問題 ,因爲我沒有計算任何東西。但它肯定是一個無效的使用或錯誤。使用std :: chrono庫來調整​​應用程序的fps,但得到奇怪的行爲

TARGET_FPS宏設置爲目標FPS,我想,然後 顯示真正的實際的FPS,以下這些行控制檯窗口中顯示我設置TARGET_FPS的價值觀,每個被關聯到最終FPS

TARGET_FPS---->FPS 

     60----->50 
     90----->50 
     100----->100 
    1000----->100 
    10000----->100 
    whatever ----->100 

即使我定義TARGET_FPS 1000000000我得到100 FPS,甚至當我將它定義到458或任何值超過100,我會得到100 FPS作爲輸出。

#include <chrono> /// to use std::chrono namespace 
#include <iostream> /// for console output 
#include <thread> /// for std::this_thread::sleep_for() 
#define TARGET_FPS 60// our target FPS  
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the  duration that defines the length of a frame 
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation 
const frame_len_type target_frame_len(1); /// we will define this constant here , to represent on frame duration (defined to avoid construction inside a loop) 
void app_logic(){ /** ... All application logic goes here ... **/} 
int main() /// our main function ! 
{ 
    using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable 
    sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end 
    while (true) 
    { 
     frame_begin = sys_clock::now(); /// there we go ! 
     app_logic(); /// lets be logical here :) 
     frame_end = sys_clock::now(); /// we are done so quick ! 
     std::this_thread::sleep_for(target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch())); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length 
     std::cout<< fsecond(1)/(sys_clock::now() - frame_begin) <<std::endl; /// this will show ass the current FPS 
    } 
    return 0; /// return to OS 
} /// end of code 
+0

[Can not](https://wandbox.org/permlink/w60n36CqXJamqgpi)[reproduce](https://wandbox.org/permlink/CpyJSQOOVchxQJwx)[it](https://wandbox.org/permlink/u1i9mfXY0BEgVzOh )實際上。你介意給更多的上下文嗎?編譯器,主機系統等。 – skypjack

+1

如果您使用的是Windows,那麼您無法使用睡眠來實現此類行爲。參見[https://stackoverflow.com/questions/85122/how-to-make-thread-sleep-less-than-a-millisecond-on-windows](https://stackoverflow.com/questions/85122/how -to-使線程睡眠低於一毫秒式窗口)。即使你正在使用別的東西,你最好花時間等待傳入的事件,而不只是睡覺。 – VTT

+0

@skypjack 操作系統:Microsoft Windows 7專業美髮 版本:6.1.7601 Service Pack 1個版本7601 類型:基於x64的PC Processeur(S):安裝1個processeur: [01]:Intel64位家庭6型號69步進1 GenuineIntel〜799 MHz BIOS版本:Insyde Corp. V1.26,2014年12月18日 –

回答

2

的std ::時辰的時間分辨率是依賴於系統:

休眠間隔過後,線程已準備好運行。如果 指定0毫秒,則線程將放棄其時間片的其餘部分 ,但保持就緒狀態。請注意,現成的線程不是 保證立即運行。因此,線程可能不運行 ,直到睡眠間隔過去一段時間。

  • C++標準庫不提供更好的擔保sleep_for,您使用的是什麼操作系統:

30.3.2/7:效果:塊調用線程相對超時(...)

後果:

  • 將FPS設置爲60時,每16.6毫秒會有一幀。所以假設你的app_logic()超快,你的線程將至少睡15.6毫秒。如果邏輯花費1毫秒執行,那麼您的精確度就是60 FPS。
  • 但是,根據API文檔如果[等待時間]大於一個滴答但小於兩個,則等待可以是任何一個到兩個滴答之間的任何值,因此平均睡眠時間將在15.6和31.2毫秒,這意味着相反,你的FPS將在60到32 FPS之間。這就解釋了爲什麼你只能達到50 FPS。

  • 當您將FPS設置爲100時,每隔10ms應該有一個幀。這低於計時器準確度。可能根本沒有睡眠。如果沒有其他線程準備好運行,該函數將立即返回,這樣您將達到最大吞吐量。如果設置更高的FPS,則會出現完全相同的情況,因爲預計的等待時間總是低於計時器準確度。結果將不會改善。

+0

非常感謝,你所描述的和我意識到的解決方案是我寫的答案,使用timeBeginPeriod() –

+0

當我使用timeBegin/EndPeriod()我得到的最低平均幀長度等於3ms使用第一個鏈接,你給,當我沒有得到15.6,所以它工作:),再次感謝 –

1

問題從Ryan Geiss解決:)

#include <chrono> /// to use std::chrono namespace 
    #include <iostream> /// for console output 
    #include <thread> /// for std::this_thread::sleep_for() 
    #include <windows.h> 
    #define TARGET_FPS 500 /// our target fps as a macro 
    const float target_fps = (float)TARGET_FPS; /// our target fps 
    float tmp_target_fps = target_fps; /// used to adjust the target fps depending on the actual real fps to reach the real target fps 
    using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the  duration that defines the length of a frame 
    using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation 
    fsecond target_frame_len(1.0f/tmp_target_fps); /// we will define this constant here , to represent on frame duration (defined to avoid construction inside a loop) 
    bool enable_fps_oscillation = true; 
    void app_logic() 
    { 
     /** ... All application logic goes here ... **/ 
    } 
    class HeighResolutionClockKeeper 
    { 
    private : 
     bool using_higher_res_timer; 
    public : 
     HeighResolutionClockKeeper() : using_higher_res_timer(false) {} 
     void QueryHeighResolutionClock() 
     { 
      if (timeBeginPeriod(1) != TIMERR_NOCANDO) 
      { 
       using_higher_res_timer = true; 
      } 
     } 
     void FreeHeighResolutionClock() 
     { 
      if (using_higher_res_timer) 
      { 
       timeEndPeriod(1); 
      } 
     } 
     ~HeighResolutionClockKeeper() 
     { 
      FreeHeighResolutionClock(); /// if exception is thrown , if not this wont cause problems thanks to the flag we put 
     } 
    }; 
    int main() /// our main function ! 
    { 
     HeighResolutionClockKeeper MyHeighResolutionClockKeeper; 
     MyHeighResolutionClockKeeper.QueryHeighResolutionClock(); 
     using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable 
     sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end 
     sys_clock::time_point start_point = sys_clock::now(); 
     float accum_fps = 0.0f; 
     int frames_count = 0; 
     while (true) 
     { 
      frame_begin = sys_clock::now(); /// there we go ! 
      app_logic(); /// lets be logical here :) 
      frame_end = sys_clock::now(); /// we are done so quick ! 
      std::this_thread::sleep_for(target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch())); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length 
      float fps = fsecond(1)/(sys_clock::now() - frame_begin) ; /// this will show ass the current FPS 

    /// obviously we will not be able to hit the exact FPS we want se we need to oscillate around until we 
    /// get a very close average FPS by time . 
      if (fps < target_fps) /// our real fps is less than what we want 
       tmp_target_fps += 0.01; /// lets asl for more ! 
      else if (fps > target_fps) /// it is more than what we want 
       tmp_target_fps -=0.01; /// lets ask for less 
      if(enable_fps_oscillation == true) 
      { 
      /// now we will adjust our target frame length for match the new target FPS 
       target_frame_len = fsecond(1.0f/tmp_target_fps); 
      /// used to calculate average FPS 
       accum_fps+=fps; 
       frames_count++; 
       /// each 1 second 
       if((sys_clock::now()-start_point)>fsecond(1.0f)) /// show average each 1 sec 
       { 
        start_point=sys_clock::now(); 
        std::cout<<accum_fps/frames_count<<std::endl; /// it is getting more close each time to our target FPS 
       } 
      } 
      else 
      { 
       /// each frame 
       std::cout<<fps<<std::endl; 
      } 
     } 
     MyHeighResolutionClockKeeper.FreeHeighResolutionClock(); 
     return 0; /// return to OS 
    } /// end of code 

我不得不在Windows平臺上添加timeBeginPeriod()timeEndPeriod(),感謝這真棒,失在最風網站http://www.geisswerks.com/ryan/FAQS/timing.html

詳情:

因爲我們不能竟然打,我們希望(非常略高於或波紋管確切的FPS,但到1000個FPS和下降到1 FPS感謝timeXPeriod(1) )因此我使用了一些額外的轉儲fps變量來調整我們追求的目標fps,增加它並減少它,這將讓我們控制實際的應用fps來以平均值命中我們的真實目標fps(您可以啓用和禁用這使用'enable_fps_oscillation'標誌),這解決了fps = 60的問題,因爲我們不能打到它(+/- 0.5),但是如果我們設置fps = 500,我們就打它,我們不需要震盪波紋管,在它之上

+0

好的額外洞察力。我已經添加了Microsoft文檔的鏈接。請注意,timeBeginPeriod()可能會影響整個系統的性能,因爲它會導致更頻繁的上下文切換。還要注意,您必須處理意外異常的風險,這可能會導致您的代碼部分沒有所需的timeEndPeriod()。因此,我建議使用助手類使用此功能,使用更安全的[RAII](https://en.m.wikipedia.org/wiki/Resource_acquisition_is_initialization)成語。這個幫手類可以方便地移植到其他平臺 – Christophe

+0

我也必須檢查timeBeginPeriod()是否工作,以便我會打電話(或不)時間結束時期 –

+0

@Christoph因爲我們實際上無法達到我們想要的確切fps (由於timeXPeriod(1),可以高達1000 fps,低至1 fps),因此我使用了一些額外的轉儲fps變量來調整目標fps,增加它並減少它,這將讓我們控制fps以平均值命中我們的真實目標fps(您可以使用'enable_fps_oscillation'標誌來啓用和禁用此功能),這可以解決fps = 60的問題,因爲我們無法達到(+/- 0.5),但如果我們設置fps = 500我們打了它,我們不需要震盪波紋管,在它之上,你怎麼看? –