参考博文:

1.帧率(FPS)计算的几种方法总结 (baidu.com)

帧率(FPS, frame per second)计算是游戏编程中常见的一个话题,因为表现在画面刷新与视觉感官上,所以相对而言,帧率非常影响用户体验。这也是很多大型3D游戏所要提升的重要点,意味着你要不断优化渲染速度与性能,不断提升画面质量。以下是几种计算帧率fps的方法。

固定时间帧数法

其实这个方法的核心是1s内刷新了多少帧,完全不考虑其他设备,相对参照来计算帧率,最准的方法也可以叫做dps,即data per second.

帧率计算公式:

1
fps = frame / elapsedTime

如果记录固定时间内的帧数,就可以计算出同步率。此种方法用得较多。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int fps()
{
static int fps = 0;
static int startTime = getTime(); //ms
static int frameCount = 0;

++frameCount;

int curTime = getTime();
if(curTime - startTime > 1000)//取固定时间为1s
{
fps = frameCount;
frameCount = 0;
startTime = curTime;
}
return fps;
}

这个固定时间为1s,其实本文的获取方法是精度比较低,也就是没有采用高精度获取时间戳的方法,在一些要求数据比较高的方法中,最好采用高精度获取时间的方法。

另一种实现方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int fps(int deltatime)
{
static int fps = 0;
static int timeLeft = 1000; //取固定时间间隔为1s
static int frameCount = 0;

++frameCount;
timeLeft -= deltaTime;
if(timeLeft < 0)
{
fps = frameCount;
frameCount = 0; //重新计算
timeLeft = 1000;
}
return fps;
}

固定帧数时间法

帧率计算公式为:

1
fps = frameNum / elapsedTime

如果每隔固定的帧数,计算帧数使用的时间,也可求出帧率。此种方法使用得较少。这个方法其实已经不能成为实时刷新的,因为帧率最好能够再1s内,如果说固定的帧数计算帧数使用的时间,那么我1s的帧率可能得等到10s后才能采集数据完毕计算出来结果。但是不可置否,这个帧率会比较稳定,变化跳动可能不会那么大。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int fps()
{
static int fps = 0;
static int frameCount = 0;
static int startTime = getTime();//ms

++frameCount;

if(frameCount >= 100)//取固定帧数为100帧
{
int curTime = getTime();
fps = frameCount / (curTime - startTime) * 1000;
startTime = curTime;
frameCount = 0;
}
return fps;
}

实时计算法

实时计算法直接使用上一帧的时间间隔进行计算,结果具有实时性,但平滑性不好。这是刷新最快的,但是很明显这个帧率极其不稳定的,因为帧与帧之间的间隔总是不会那么稳定。

实现代码如下:

1
2
3
4
5
6
int fps(int deltaTime)//ms
{
int fps = static_cast<int>(1.f / deltaTime * 1000);
//别忘了先转换为浮点数,否则会有精度损失
return fps;
}

总平均法

总平均法使用全局帧数除以全局时间,以求出帧率。这个刷新就更慢了,这个方法的使用可能是放在一些对帧率这个参数要求没那么高的场景中。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int startTime = getTime();

int fps()
{
static int frameCount = 0;

++frameCount;

int deltaTime = getTime() - startTime();
int fps = static_cast<int>(frameCount * 1.f / deltaTime * 1000);
//别忘了先转换为浮点数,否则会有精度损失
return fps;
}

精确采样法

精确采样法采样前N个帧,然后计算平均值。此种方法需要额外的内存空间,所以不常用。一般而言,大都数实验场景中不会用如此复杂的方法。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int fps(int deltaTime)//ms
{
static std::queue<int> q;
static int sumDuration = 0; //ms

int fps = 0;
if(q.size() < 100)//设置样本数为100
{
sumDuration += deltaTime;
q.push(deltaTime);
fps = statci_cast<int>(q.size() * 1.f / sumDuration * 1000.f);
//别忘了先转换为浮点数,否则会有精度损失
}
else
{
sumDuration -= q.front();
sumDuration += deltaTime;
sumDuration.pop();
sumDuration.push(deltaTime);
fps = static_cast<int>(100.f / sumDuration * 1000.f);
//别忘了先转换为浮点数,否则会有精度损失
}
return fps;
}

平均采样法

平均采样法利用上次的统计结果,克服了精确采样法需要使用额外空间的缺点。此种方法较常用。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int fps(int deltaTime)//ms
{
static float avgDuration = 0.f;
static alpha = 1.f / 100.f;//采样数设置为100
static int frameCount = 0;

++frameCount;

int fps = 0;
if(1 == frameCount)
{
avgDuration = static_cast<float>(deltaTime);
}
else
{
avgDuration = avgDuration * (1 - alpha) + deltaTime * alpha;
}
fps = static_cast<int>(1.f / avgDuration * 1000);
return fps;
}

帧率的计算应该来说不能影响线程,进程中的性能,不要把这部分代码放在你的业务逻辑中,这样子其实又包含了你业务逻辑执行完毕后的运行时间,显得有点臃肿,并且代码不是那么优雅。最好是创建一个线程去计算帧率,这样会显得比较优雅。