2015-02-09 86 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