2015-02-09 63 views
2

我想計算我的程序的FPS。儘管給定的最大FPS超出範圍,我無法解釋原因。我用MAX_FPS設置爲60進行測試,我總是得到約63顯示。超過最大FPS

{...} 
int frameCount = 0; 
long time = System.currentTimeMillis(); 
while(running) { 
    try { 
     {...} // do something 
     Thread.sleep(1000/MAX_FPS); 
     frameCount++; 
     if (System.currentTimeMillis() - time >= 1000) { 
      {...} // display FPS w/ frameCount 
      frameCount = 0; 
      time = System.currentTimeMillis(); 
     } 
    } catch (InterruptedException ex) { 
     System.err.println(ex.toString()); 
    } 
} 
{...} 
+2

1000/60在整數數學中爲16,而1000/16大致爲62.5。假設線程也睡了接近16毫秒(也許少一些),如果你的循環不會花這麼長時間執行迭代,如果在999毫秒倒數第二幀成爲通過允許另一個更新忙裏偷閒,那麼這個循環經常可以每秒達到63次迭代是有意義的。編輯:我只是做出這個答案。 – NESPowerGlove 2015-02-09 22:57:38

+0

@Astraioz - 你肯定需要'1_000_000_000'幾納秒? (或者使用'TimeUnit'做轉換) – 2015-02-09 23:13:29

回答

1

基於NESPowerGlove的答案,您在達到目標FPS時遇到的問題是您必須選擇一個整數值才能撥打Thread.sleep() - 如果您選擇16,則會獲得約63fps;如果你選擇17,你會得到約58fps。

有兩種解決方案,我能想到的是:

  1. 實施某種形式的負反饋迴路來控制FPS的。例如,我發現以下PI(比例+積分)控制器保持在FPS 周圍 60:通過試錯法找到

    final long sleepTime = (long) (1000.0/TARGET_FPS); 
    
    System.out.println("Sleep time is " + sleepTime); 
    long time = System.nanoTime(); 
    long delta = 0; 
    double cumulativeFpsError = 0.0; 
    for (int frameCount = 0; true; ++frameCount) { 
        Thread.sleep((long) (sleepTime + delta)); 
        long elapsed = System.nanoTime() - time; 
        if (elapsed >= 1_000_000_000) { 
        double fps = (double) frameCount/elapsed * 1e9; 
        System.out.println(fps); 
    
        cumulativeFpsError += (fps - 60); 
        delta += (fps - TARGET_FPS) * 0.55 + 0.14 * cumulativeFpsError; 
        frameCount = 0; 
        time += elapsed; 
        } 
    } 
    

0.550.14的值。可能有更好的值(但至少看起來大致穩定)。 FPS輸出:

61.08042479827653 
61.817816897275485 
58.42717726642977 
62.0654826347193 
58.43759367657694 
62.07263954479402 
58.444556146850026 
62.05489635777375 
58.4438970272065 
62.05784933619571 
58.45590905841833 
62.069491426232766 
58.44381852435569 
62.07438904528996 
... 

實際上,積分項沒有做太多在所有 - 大概是因爲睡眠值的尺寸的步驟1.

  • 僅改變

    使用不同的方法可以更精確地睡眠。例如,How to suspend a java thread for a small period of time, like 100 nanoseconds?建議了一些替代選項,例如輪詢System.nanoTime(),直到超過某個截止日期。例如

    long time = System.nanoTime(); 
    for (int frameCount = 0; true; ++frameCount) { 
        long end = System.nanoTime() + (long) (1_000_000_000.0/TARGET_FPS); 
        while (System.nanoTime() < end); 
        long elapsed = System.nanoTime() - time; 
        if (elapsed >= 1_000_000_000) { 
        double fps = (double) frameCount/elapsed * 1e9; 
        System.out.println(fps); 
    
        frameCount = 0; 
        time = System.nanoTime(); 
        } 
    } 
    
  • 這似乎更好地工作,與FPS不到60徘徊:

    58.99961555850502 
    59.99966304189236 
    59.99942898543434 
    59.99968068169941 
    59.99968770162551 
    59.99919595077507 
    59.99945862488483 
    59.999679241714766 
    59.99753134157542 
    59.99963898217224 
    59.999265728986 
    ... 
    

    此方法的缺點可能是while循環將是相當火爆。

    當然,您可以採用某種混合方法,在大部分等待時使用Thread.sleep,然後使用熱門while循環獲得更精確的延遲。

    +0

    感謝您的詳細解答。這幫助了很多! – Astraioz 2015-02-10 01:07:22

    2

    1000/60在整數數學中爲16,1000/16爲大致62.5。假設線程的睡眠時間接近16毫秒(可能稍微少一點),並且如果你的循環不需要很長時間來執行一個迭代,並且倒數第二幀在999毫秒內通過,允許另一個更新潛入,那麼這個循環經常可以每秒達到63次迭代是有意義的。

    +0

    @Astraioz - 這就是答案 - 礦井或NESPowerGlove的 - 你所描述既作爲回答您的問題:) – 2015-02-09 23:12:20

    +0

    @NESPowerGlove @AndyTurner我很抱歉,我沒有檢查對。 NESPowerGlove解釋了我的問題,但沒有解決它。你也沒有。是的,'System.nanoTime()'是更正確的方法,但不能解決我的問題。 – Astraioz 2015-02-09 23:23:54

    +0

    我需要再次記錄自己,對不起。 NESPowerGlove有正確的答案。此外,我想知道是否有方法來衡量它,因此它不會超過給定的最大值。 – Astraioz 2015-02-09 23:27:18

    2

    您可能想用System.nanoTime()代替。不是爲了任何額外的精度,而是因爲它是一種更準確的測量流逝時間的方法。請參閱Kevin Bourillion對How do I measure time elapsed in Java?的回答。

    +0

    爲什麼downvote? – 2015-02-09 23:16:30

    +0

    我沒有downvote。但也許是因爲你沒有真正回答這個問題,這是爲什麼FPS的預期數量被超過。我只是猜測。你不得不問下來,正如我所說的那樣,那不是我。 – 2015-02-09 23:46:06

    +0

    @DavidWallace - 我將它添加爲答案,因爲OP說我的評論爲他修正了它。我有點驚訝,但你走了。 – 2015-02-09 23:51:43