C++:计算游戏中的总帧数

4

虽然标题不太好,但我不知道该怎么取名。

无论如何,我正在使用long int计算我的游戏中的总帧数(以便我可以计算平均FPS)。万一游戏持续时间非常长,我应该怎么做才能确保我的long int不会超过其限制?如果它确实超过了限制会发生什么?

谢谢。


1
为什么需要整个游戏的平均值?为什么不只保持最后几秒钟的计数,然后从那里不再烦恼呢? - Platinum Azure
1
你在http://gamedev.stackexchange.com/上询问会不会更有好运呢?这并非反问,我是真的在想。 - Jake
1
@Jake,这个问题实际上与游戏没有任何关系,但我不知道该如何命名并提出问题。 - user542687
1
我猜发生的情况在某种程度上取决于您的编译器和系统。我建议将数字设置为最大值,然后递增以查看会发生什么。 - Jesse Vogt
2
@Jay:我不这么认为。要溢出它,你需要以每秒30帧的速度玩1169884834710年。 - BlackBear
显示剩余3条评论
6个回答

8
这个问题适用于任何类型的计数器。
对于你的具体问题,我不会担心。
在大多数最糟糕的情况下(在32位计算机/游戏机上),一个long int可以计数到20亿及更多。假设你的游戏每秒执行1000帧(这很多!),那么需要20000000秒才能溢出你的计数器:超过5000小时,超过231天。
我敢肯定,如果你试图运行它那么长时间,一些其他问题会导致你的游戏停止!

@peoro 是的,我自己也做了一些计算,但这只是以防万一。当计数器被过度增加时会发生什么? - user542687
@Jay:如果它被声明为无符号的,它将返回0。如果没有,则会变成负数。这种情况的确切影响取决于您的代码。 - luiscubal
1
@Jay:当整数溢出时(即它超过了它可以包含的最大值),它会从它可以包含的最小值重新开始。发生什么取决于您对整数执行的操作,最有可能的是您的FPS指示器将显示您的游戏以-X帧每秒运行。 - peoro
1
@luiscubal @peoro 在C和C++中,有符号整数溢出会导致未定义的行为。它不能保证环绕到最小的负数。然而,无符号整数溢出是被定义明确的。请参阅此问题 - ollb

7
我建议使用指数加权移动平均法xponentially-weighted moving average。这种方法可以一举解决两个问题:避免累积大量数据的问题,同时也可以适应最近的行为,因此2010年平均100fps的累积平均值不会使2011年的2fps速率看起来合理 :).

5
整个游戏过程中的平均帧率似乎不是一个非常有用的统计数据。通常,您会希望测量高峰和低谷,例如最高帧率/最低帧率以及在阈值以下和以上花费的帧数。
但实际上我并不担心这个问题。即使您只使用32位无符号整数,您的游戏也可以以60fps运行19884小时,直到溢出为止。你应该没问题。
编辑:
在这种情况下检测溢出的最佳方法是检查并查看整数在递增后是否减小了。如果是,您可以再保留一个计数器,用于记录溢出次数。

2
如果long int是32位的,则最大值为2^31-1,因此以1毫秒更新一次,它将在24.9天内溢出,而不是231[2^31/1000/60/60/24]。
希望这不会太脱离主题... 对于游戏来说,这可能并不是一个问题,但是对于其他应用程序来说却是。要注意的常见错误是执行类似以下代码的操作:
extern volatile uint32_t counter;
uint32_t one_second_elapsed = counter + 1000;
while ( counter < one_second_elapsed ) do_something();

如果计数器加上1000会溢出,那么do_something()将不会被调用。检查的方法是:
uint32_t start = counter;
while ( counter - start < 1000 ) do_something();

1

在进行算术运算时,您可以积极检查是否存在溢出。例如,SafeInt 可以为您完成此操作。当然,性能比 i++ 差。

但是,如果您始终按一递增,则不太可能发生 32 位整数溢出。


1
我不建议在这种特定情况下采用这样的方法。避免溢出的其他解决方案可能是使用更大的类型来存储值:long long,或者一些高级数学库提供的动态长度整数。但对于这种情况来说,这并不值得。这只会使事情变得更慢、更复杂,总体上变得更糟,而没有任何收益。 - peoro
我同意 - 所以我说性能会下降。也许我应该补充说明,使用 SafeInt 将需要一个 try-catch 块,因此性能可能会显著下降。但问题是如何检测溢出。对于特定情况,我认为我们都同意这种问题不太可能发生,您应该忽略它。 - Christoph

0

最好使用少量帧的平均值。您提到要计算平均值,但实际上没有必要保留这么多样本来计算平均值。只需在一段较短的时间内(可以是10-50帧之间,我们通常使用16)保持帧时间的累加总和即可。然后,您可以使用该总和来计算每秒帧数的平均值。这种方法还有助于平滑帧时间报告,使数字不会跳动。但需要注意的是,如果平均时间太长,帧速率峰值将变得更加“隐藏”,这意味着如果那些帧仅偶尔发生,则可能更难以发现导致帧速率下降的帧。

我认为像这样的代码就足够了(以下是未经测试的代码):

// setup some variables once
const int Max_samples = 16;    // keep at most 16 frametime samples
int FPS_Samples = 0;
int Current_sample = 0;
int Total_frametime = 0.0f;
float Frametimes[Max_samples];
for ( int i = 0; i < Max_samples; i++ ) {
    Frametimes[i] = 0.0f;

那么当你计算帧时间时,你可以像这样做:

// current_frametime is the new frame time for this frame
Total_frametime -= Frametimes[Current_sample];
Total_frametime += current_frametime;
Frametimes[Current_sample] = current_frametime;
Current_sample = ( Current_sample + 1 ) % Max_samples;   // move to next element in array

Frames_per_second = Max_samples / Total_frametime;

这只是一个初步版本,可能需要一些错误检查,但它能够传达基本思想。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接