计算游戏中的每秒帧数

在游戏中计算每秒帧数的algorithm是什么? 我想在屏幕的angular落显示它作为一个数字。 如果我只看看渲染最后一帧需要多长时间,数字变化太快。

奖励分数,如果您的答案更新每帧,并没有收敛不同,当帧率增加与减less。

你需要一个平滑的平均值,最简单的方法是采取当前的答案(绘制最后一帧的时间),并将其与前一个答案结合起来。

// eg. float smoothing = 0.9; // larger=more smoothing measurement = (measurement * smoothing) + (current * (1.0-smoothing)) 

通过调整0.9 / 0.1的比例,你可以改变“时间常数”,即数字响应变化的速度。 赞成旧答案的较大比例给出了较慢的平滑变化,赞成新答案的很大一部分给出了更快的变化值。 显然这两个因素必须加一!

这是我在许多游戏中使用的。

 #define MAXSAMPLES 100 int tickindex=0 int ticksum=0; int ticklist[MAXSAMPLES]; /* need to zero out the ticklist array before starting */ /* average will ramp up until the buffer is full */ /* returns average ticks per frame over the MAXSAMPLES last frames */ double CalcAverageTick(int newtick) { ticksum-=ticklist[tickindex]; /* subtract value falling off */ ticksum+=newtick; /* add new value */ ticklist[tickindex]=newtick; /* save new value so it can be subtracted later */ if(++tickindex==MAXSAMPLES) /* inc buffer index */ tickindex=0; /* return average */ return((double)ticksum/MAXSAMPLES); } 

那当然

 frames / sec = 1 / (sec / frame) 

但是,正如你所指出的那样,渲染单个帧所花费的时间有很大的变化,并且从UI的angular度来看,更新帧速率下的fps值根本不可用(除非该数字非常稳定)。

你想要的可能是移动平均数或者某种分档/重置计数器。

例如,您可以维护一个队列数据结构,该结构为最后的30,60,100,或者你所拥有的每一帧保存渲染时间(甚至可以devise它,以便在运行时可以调整极限)。 要确定一个体面的fps近似值,您可以确定队列中所有渲染时间的平均fps:

 fps = # of rendering times in queue / total rendering time 

当你完成渲染一个新的框架,你排队一个新的渲染时间和出列旧的渲染时间。 或者,只有当渲染时间总计超过某个预设值(例如1秒)时才可以出队。 你可以保持“最后的FPS价值”和最后更新的时间戳,所以你可以触发何时更新FPS数字,如果你愿意。 虽然如果你有一致的格式,如果有移动平均值,在每一帧上打印“瞬时平均值”fps可能是可以的。

另一种方法是有一个重置计数器。 保持精确(毫秒)的时间戳,帧计数器和fps值。 当你完成渲染一个帧,增加计数器。 当计数器达到预先设定的限制(例如100帧)或自从时间戳已经超过某个预设值(例如1秒)以来的时间时,计算fps:

 fps = # frames / (current time - start time) 

然后将计数器重置为0,并将时间戳设置为当前时间。

每次渲染一个屏幕时增加一个计数器,并在一定的时间间隔内清除该计数器,以计算帧率。

IE浏览器。 每3秒钟,计数器/ 3,然后清除计数器。

至less有两种方法可以做到这一点:


第一个是在我之前提到的其他人。 我认为这是最简单和首选的方式。 你只是跟踪

  • cn:你已经渲染了多less帧
  • time_start:你开始计算的时间
  • time_now:当前时间

在这种情况下计算fps就像评估这个公式一样简单:

  • FPS = cn /(time_now – time_start)。

那么有一天你可能会喜欢用这种超酷的方式:

假设你有'我'的框架考虑。 我将用这个表示法:f [0],f [1],…,f [i-1]来描述渲染帧0,帧1,…帧(i-1) ) 分别。

 Example where i = 3 |f[0] |f[1] |f[2] | +----------+-------------+-------+------> time 

那么,i帧之后的fps的math定义将是

 (1) fps[i] = i / (f[0] + ... + f[i-1]) 

和相同的公式,但只考虑i-1帧。

 (2) fps[i-1] = (i-1) / (f[0] + ... + f[i-2]) 

现在这里的诀窍就是修改公式(1)右边的方式,它将包含公式(2)的右边并replace它的左边。

像这样(如果你把它写在纸上,你应该看的更清楚):

 fps[i] = i / (f[0] + ... + f[i-1]) = i / ((f[0] + ... + f[i-2]) + f[i-1]) = (i/(i-1)) / ((f[0] + ... + f[i-2])/(i-1) + f[i-1]/(i-1)) = (i/(i-1)) / (1/fps[i-1] + f[i-1]/(i-1)) = ... = (i*fps[i-1]) / (f[i-1] * fps[i-1] + i - 1) 

所以根据这个公式(我的math派生技能有点生疏),为了计算新的fps,你需要知道前一帧的fps,渲染最后一帧的持续时间和帧数渲染。

这对大多数人来说可能是过度的,这就是为什么当我实现它时我没有发布它。 但它非常强大和灵活。

它存储具有最后帧时间的队列,所以它可以准确地计算平均FPS值,而不仅仅考虑最后一帧。

它也允许你忽略一个框架,如果你正在做的事情,你知道是人为地搞砸了框架的时间。

它还允许您在运行时更改要存储在队列中的帧数,以便您可以随时对其进行testing,这对您来说是最有价值的。

 // Number of past frames to use for FPS smooth calculation - because // Unity's smoothedDeltaTime, well - it kinda sucks private int frameTimesSize = 60; // A Queue is the perfect data structure for the smoothed FPS task; // new values in, old values out private Queue<float> frameTimes; // Not really needed, but used for faster updating then processing // the entire queue every frame private float __frameTimesSum = 0; // Flag to ignore the next frame when performing a heavy one-time operation // (like changing resolution) private bool _fpsIgnoreNextFrame = false; //============================================================================= // Call this after doing a heavy operation that will screw up with FPS calculation void FPSIgnoreNextFrame() { this._fpsIgnoreNextFrame = true; } //============================================================================= // Smoothed FPS counter updating void Update() { if (this._fpsIgnoreNextFrame) { this._fpsIgnoreNextFrame = false; return; } // While looping here allows the frameTimesSize member to be changed dinamically while (this.frameTimes.Count >= this.frameTimesSize) { this.__frameTimesSum -= this.frameTimes.Dequeue(); } while (this.frameTimes.Count < this.frameTimesSize) { this.__frameTimesSum += Time.deltaTime; this.frameTimes.Enqueue(Time.deltaTime); } } //============================================================================= // Public function to get smoothed FPS values public int GetSmoothedFPS() { return (int)(this.frameTimesSize / this.__frameTimesSum * Time.timeScale); } 

这里的答案很好。 你如何实现它取决于你需要什么。 我更喜欢上面的那个人自己的“时间=时间* 0.9 +最后一帧* 0.1”。

但是我个人喜欢把我的平均水平加到更新的数据上,因为在比赛中,SPIKES是最难压扁的,因此对我来说最感兴趣。 所以我会使用更像.7 \ .3分割的东西,使得尖峰显示速度要快得多(尽pipe它的效果也会更快地下降,见下文)

如果您的注意力集中在渲染时间上,那么.9.1分割效果相当不错,但它往往更加平滑。 尽pipe对游戏玩法/人工智能/物理尖峰更为关注,因为这通常会让你的游戏显得波涛汹涌(如果我们不低于20fps,这通常比低帧率更差)

所以,我会做什么也是这样的:

 #define ONE_OVER_FPS (1.0f/60.0f) static float g_SpikeGuardBreakpoint = 3.0f * ONE_OVER_FPS; if(time > g_SpikeGuardBreakpoint) DoInternalBreakpoint() 

(用任何你认为是不可接受的数量级来填写3.0f)这将使你find并解决 FPS问题。

你可以保留一个计数器,在渲染每一帧之后增加计数器,然后当你在一个新的秒钟时重置计数器(把以前的值存储为最后一帧的渲染帧数)

我如何做到这一点!

 boolean run = false; int ticks = 0; long tickstart; int fps; public void loop() { if(this.ticks==0) { this.tickstart = System.currentTimeMillis(); } this.ticks++; this.fps = (int)this.ticks / (System.currentTimeMillis()-this.tickstart); } 

换句话说,一个嘀嗒钟跟踪嘀嗒。 如果这是第一次,它需要现在的时间,并把它放在'tickstart'。 在第一次打勾之后,它使得variables“fps”等于打勾时钟的多less刻度除以时间减去第一次打勾的时间。

Fps是一个整数,因此“(int)”。

JavaScript的:

 // Set the end and start times var start = (new Date).getTime(), end, FPS; /* ... * the loop/block your want to watch * ... */ end = (new Date).getTime(); // since the times are by millisecond, use 1000 (1000ms = 1s) // then multiply the result by (MaxFPS / 1000) // FPS = (1000 - (end - start)) * (MaxFPS / 1000) FPS = Math.round((1000 - (end - start)) * (60 / 1000)); 

将计数器设置为零。 每次你画一个帧增加计数器。 每秒钟后打印计数器。 起泡,冲洗,重复。 如果你想要额外的信用,保持一个跑步计数器,并除以运行平均值的总秒数。

存储一个开始时间,并增加你的framecounter每个循环一次? 每隔几秒钟您就可以打印framecount /(Now – starttime),然后重新初始化它们。

编辑:哎呀。 双ninja'ed

在(c ++ like)伪代码中,这两个是我在工业image processing应用程序中使用的,这些应用程序必须从一组外部触发的相机处理图像。 “帧速率”的变化有不同的来源(带上生产速度越来越慢),但问题是一样的。 (我假设你有一个简单的timer.peek()调用,从应用程序启动或上次调用开始,给你类似msec(nsec?)的nr值)

解决scheme1:速度快但不更新每一帧

 do while (1) { ProcessImage(frame) if (frame.framenumber%poll_interval==0) { new_time=timer.peek() framerate=poll_interval/(new_time - last_time) last_time=new_time } } 

解决scheme2:每帧更新,需要更多的内存和CPU

 do while (1) { ProcessImage(frame) new_time=timer.peek() delta=new_time - last_time last_time = new_time total_time += delta delta_history.push(delta) framerate= delta_history.length() / total_time while (delta_history.length() > avg_interval) { oldest_delta = delta_history.pop() total_time -= oldest_delta } } 
 qx.Class.define('FpsCounter', { extend: qx.core.Object ,properties: { } ,events: { } ,construct: function(){ this.base(arguments); this.restart(); } ,statics: { } ,members: { restart: function(){ this.__frames = []; } ,addFrame: function(){ this.__frames.push(new Date()); } ,getFps: function(averageFrames){ debugger; if(!averageFrames){ averageFrames = 2; } var time = 0; var l = this.__frames.length; var i = averageFrames; while(i > 0){ if(l - i - 1 >= 0){ time += this.__frames[l - i] - this.__frames[l - i - 1]; } i--; } var fps = averageFrames / time * 1000; return fps; } } }); 

以下是我如何做(Java):

 private static long ONE_SECOND = 1000000L * 1000L; //1 second is 1000ms which is 1000000ns LinkedList<Long> frames = new LinkedList<>(); //List of frames within 1 second public int calcFPS(){ long time = System.nanoTime(); //Current time in nano seconds frames.add(time); //Add this frame to the list while(true){ long f = frames.getFirst(); //Look at the first element in frames if(time - f > ONE_SECOND){ //If it was more than 1 second ago frames.remove(); //Remove it from the list of frames } else break; /*If it was within 1 second we know that all other frames in the list * are also within 1 second */ } return frames.size(); //Return the size of the list } 

比使用大量旧帧率更好的系统是做这样的事情:

 new_fps = old_fps * 0.99 + new_fps * 0.01 

这种方法使用的内存less得多,所需的代码less得多,并且比旧的帧率更重视最近的帧率,同时仍然平滑突然帧率变化的影响。

这是一个完整的例子,使用Python(但很容易适应任何语言)。 它使用马丁答案中的平滑方程,所以几乎没有内存开销,而且我select了适合我的值(随意使用常量来适应您的使用情况)。

 import time SMOOTHING_FACTOR = 0.99 MAX_FPS = 10000 avg_fps = -1 last_tick = time.time() while True: # <Do your rendering work here...> current_tick = time.time() # Ensure we don't get crazy large frame rates, by capping to MAX_FPS current_fps = 1.0 / max(current_tick - last_tick, 1.0/MAX_FPS) last_tick = current_tick if avg_fps < 0: avg_fps = current_fps else: avg_fps = (avg_fps * SMOOTHING_FACTOR) + (current_fps * (1-SMOOTHING_FACTOR)) print(avg_fps) 
Interesting Posts