Algorithm 计算游戏中每秒的帧数

Algorithm 计算游戏中每秒的帧数,algorithm,Algorithm,游戏中计算每秒帧数的好算法是什么?我想在屏幕的角落里把它显示为一个数字。如果我只看渲染最后一帧所花的时间,数字变化太快了 如果您的答案更新了每一帧,并且在帧速率增加与减少时不会以不同的方式收敛,则可获得额外分数。您需要平滑的平均值,最简单的方法是获取当前答案(绘制最后一帧的时间)并将其与上一个答案相结合 // eg. float smoothing = 0.9; // larger=more smoothing measurement = (measurement * smoothing) +

游戏中计算每秒帧数的好算法是什么?我想在屏幕的角落里把它显示为一个数字。如果我只看渲染最后一帧所花的时间,数字变化太快了


如果您的答案更新了每一帧,并且在帧速率增加与减少时不会以不同的方式收敛,则可获得额外分数。

您需要平滑的平均值,最简单的方法是获取当前答案(绘制最后一帧的时间)并将其与上一个答案相结合

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

通过调整0.9/0.1比率,您可以更改“时间常数”——即数字响应变化的速度。支持旧答案的分数越大,变化越慢,变化越平滑;支持新答案的分数越大,变化值越快。显然,这两个因素必须加在一起

每次渲染屏幕时递增一个计数器,并在要测量帧速率的某个时间间隔内清除该计数器


即,每隔3秒,获取计数器/3,然后清除计数器。

将计数器设置为零。每次绘制帧时,计数器都会递增。每秒钟后打印计数器。起泡,冲洗,重复。如果你想获得额外的积分,请保留一个运行计数器并除以运行平均数的总秒数。

你可以保留一个计数器,在渲染每个帧后递增,然后在新的一秒上重置计数器(将以前的值存储为渲染帧的最后一秒)

存储开始时间并在每个循环中增加帧计数器一次?每隔几秒钟,您就可以打印帧数/(Now-starttime),然后重新初始化它们

编辑:哎呀。双人忍者装

当然可以

frames / sec = 1 / (sec / frame)
但是,正如您所指出的,渲染单个帧所需的时间有很多变化,从UI的角度来看,以帧速率更新fps值根本不可用(除非数量非常稳定)

你想要的可能是一个移动平均线或某种分类/重置计数器

例如,您可以维护一个队列数据结构,该结构保存最近30、60、100帧或其他帧的渲染时间(您甚至可以将其设计为在运行时可调整限制)。要确定合适的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,并将时间戳设置为当前时间。

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

#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);
}
在(类似c++的)伪代码中,这两个是我在工业图像处理应用程序中使用的,这些应用程序必须处理来自一组外部触发相机的图像。“帧速率”的变化有不同的来源(传送带上的生产速度较慢或较快),但问题是相同的。(我假设您有一个简单的timer.peek()调用,该调用提供了自应用程序启动或最后一次调用以来的nr msec(nsec?)

解决方案1:快速但不是每帧都更新

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
    }
}
解决方案2:每帧更新一次,需要更多内存和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
   }
} 

这里有很好的答案。您如何实现它取决于您需要它做什么。我更喜欢上面那个家伙说的“时间=时间*0.9+最后一帧*0.1”的跑步平均水平

然而,我个人喜欢在更新的数据上更重地衡量我的平均值,因为在游戏中,最难压制的是尖峰,因此我最感兴趣。因此,我会使用更像.7\.3分割的东西,使尖峰显示得更快(尽管它的效果也会更快地从屏幕上消失..见下文)

如果您的重点是渲染时间,那么.9.1分割工作非常好,b/c它往往更平滑。虽然对于游戏性/人工智能/物理尖峰更令人担忧,因为这通常会让你的游戏看起来波涛汹涌(这通常比低帧速率更糟糕,假设我们的帧速率不低于20 fps)

所以,我要做的就是添加如下内容:

#define ONE_OVER_FPS (1.0f/60.0f)
static float g_SpikeGuardBreakpoint = 3.0f * ONE_OVER_FPS;
if(time > g_SpikeGuardBreakpoint)
    DoInternalBreakpoint()
(在3.0f中填入您认为不可接受的峰值大小)
这将让您在帧结束时发现并解决FPS问题。

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


第一个是其他人在我前面提到的。 我认为这是最简单也是最受欢迎的方式。你只是想知道

  • cn:渲染多少帧的计数器
  • 开始计时:从开始计时起的时间
  • time\u now:当前时间
在这种情况下,计算fps与计算以下公式一样简单:

  • FPS=cn/(现在时间-开始时间)

还有一种你将来可能会喜欢使用的超级酷的方式:

我们假设你有“我”的框架。我将使用以下符号:f[0]、f[1]、…、f[I-1]分别描述渲染第0帧、第1帧、…、第(I-1)帧所需的时间

Example where i = 3

|f[0]      |f[1]         |f[2]   |
+----------+-------------+-------+------> time
然后,在i帧之后的fps的数学定义将是

(1) fps[i]   = i     / (f[0] + ... + f[i-1])
和相同的公式,但只考虑i-1帧

(2) fps[i-1] = (i-1) / (f[0] + ... + f[i-2]) 
这里的技巧是修改s中公式(1)的右侧
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)
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;
        }
    }

});
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);
}
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
// 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));
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)
let getTime = () => {
    return new Date().getTime();
} 

let frames: any[] = [];
let previousTime = getTime();
let framerate:number = 0;
let frametime:number = 0;

let updateStats = (samples:number=60) => {
    samples = Math.max(samples, 1) >> 0;

    if (frames.length === samples) {
        let currentTime: number = getTime() - previousTime;

        frametime = currentTime / samples;
        framerate = 1000 * samples / currentTime;

        previousTime = getTime();

        frames = [];
    }

    frames.push(1);
}
statsUpdate();

// Print
stats.innerHTML = Math.round(framerate) + ' FPS ' + frametime.toFixed(2) + ' ms';
fpsObject = {
  maxSamples: 100,
  tickIndex: 0,
  tickSum: 0,
  tickList: []
}
calculateFps(currentFps: number): number {
  this.fpsObject.tickSum -= this.fpsObject.tickList[this.fpsObject.tickIndex] || 0
  this.fpsObject.tickSum += currentFps
  this.fpsObject.tickList[this.fpsObject.tickIndex] = currentFps
  if (++this.fpsObject.tickIndex === this.fpsObject.maxSamples) this.fpsObject.tickIndex = 0
  const smoothedFps = this.fpsObject.tickSum / this.fpsObject.maxSamples
  return Math.floor(smoothedFps)
}
this.fps = this.calculateFps(this.ticker.FPS)