Подтвердить что ты не робот

Вычисление кадров в секунду в игре

Какой хороший алгоритм для вычисления кадров в секунду в игре? Я хочу показать его как число в углу экрана. Если я просто посмотрю, сколько времени потребовалось для рендеринга последнего кадра, число изменяется слишком быстро.

Бонусные баллы, если ваш ответ обновляет каждый фрейм и не сходится иначе, когда частота кадров увеличивается и уменьшается.

4b9b3361

Ответ 1

Вам нужно сглаженное среднее значение, самый простой способ - взять текущий ответ (время рисования последнего кадра) и объединить его с предыдущим ответом.

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

Регулируя соотношение 0,9/0,1, вы можете изменить "постоянную времени" - это то, как быстро число реагирует на изменения. Большая доля в пользу старого ответа дает более медленное изменение, большая доля в пользу нового ответа дает более быстрое изменение значения. Очевидно, что два фактора должны добавить к одному!

Ответ 2

Это то, что я использовал во многих играх.

#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);
}

Ответ 3

Ну, конечно

frames / sec = 1 / (sec / frame)

Но, как вы указываете, существует много изменений в времени, которое требуется для рендеринга одного кадра, и с точки зрения пользовательского интерфейса обновление значения 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)

Затем reset счетчик 0 и установите временную метку на текущее время.

Ответ 4

Увеличивайте счетчик каждый раз, когда вы визуализируете экран, и очищаете этот счетчик на некоторый временной интервал, по которому вы хотите измерить частоту кадров.

Т.е. Каждые 3 секунды получите счетчик /3, а затем очистите счетчик.

Ответ 5

Существует как минимум два способа сделать это:


Первый - это тот, который мы упоминали выше. Я считаю это самым простым и предпочтительным способом. Вы просто отслеживаете

  • cn: счетчик количества кадров, которые вы просмотрели
  • time_start: время с момента начала подсчета
  • time_now: текущее время

Вычисление fps в этом случае так же просто, как оценка этой формулы:

  • FPS = cn/(time_now - time_start).

Тогда есть здоровый способ uber, который вы могли бы использовать в какой-то день:

Скажем, у вас есть "i" кадры для рассмотрения. Я буду использовать эти обозначения: f [0], f [1],..., f [i-1], чтобы описать, сколько времени потребовалось для рендеринга кадра 0, кадра 1,..., кадра (i-1 ) соответственно.

Example where i = 3

|f[0]      |f[1]         |f[2]   |
+----------+-------------+-------+------> time

Тогда математическое определение fps после я кадров было бы

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

И та же формула, но только с учетом кадров i-1.

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

Теперь трюк состоит в том, чтобы изменить правую часть формулы (1) таким образом, чтобы она содержала правую часть формулы (2) и заменила ее на левую сторону.

Таким образом (вы должны видеть это более четко, если вы напишете его на бумаге):

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)

Таким образом, согласно этой формуле (моя математика, использующая навык, немного ржавая), чтобы вычислить новые fps, вам нужно знать fps из предыдущего кадра, продолжительность, которую он потребовал, чтобы отобразить последний кадр и количество кадров вы сделали.

Ответ 6

Это может быть излишним для большинства людей, поэтому я не опубликовал его, когда я его реализовал. Но он очень надежный и гибкий.

Он хранит очередь с последним временем кадра, поэтому он может точно рассчитать среднее значение FPS намного лучше, чем просто учитывать последний кадр.

Он также позволяет игнорировать один кадр, если вы делаете то, что, как вы знаете, искусственно испортите это время кадра.

Он также позволяет вам изменять количество кадров для хранения в очереди при его запуске, поэтому вы можете проверить его на лету, что для вас самое лучшее.

// Number of past frames to use for FPS smooth calculation - because 
// Unity 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);
}

Ответ 7

Хорошие ответы здесь. Только то, как вы его реализуете, зависит от того, для чего вам это нужно. Я предпочитаю бегущий средний сам "time = time" 0.9 + last_frame * 0,1 "от парня выше.

однако мне лично нравится весовать в среднем больше по отношению к более новым данным, потому что в игре SPIKES сложнее всего сквош и, таким образом, меня интересует. Поэтому я бы использовал что-то большее, как .7 \.3 split, будет увеличиваться всплеск гораздо быстрее (хотя эффект также будет быстрее выходить за пределы экрана.. см. Ниже)

Если ваше внимание сосредоточено на времени RENDERING, то разделение .9.1 работает довольно красиво b/c, оно имеет тенденцию быть более гладким. То, что для геймплея/AI/физических шипов гораздо больше беспокоит, поскольку THAT обычно будет причиной того, что ваша игра выглядит нестабильной (что часто бывает хуже, чем низкая частота кадров, предполагая, что мы не опускаемся ниже 20 кадров в секунду)

Итак, что бы я сделал, также добавьте что-то вроде этого:

#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 в конце кадра, в котором они происходят.

Ответ 8

Вы можете сохранить счетчик, увеличивать его после каждого кадра, а затем reset счетчик, когда вы находитесь в новой секунде (сохраняя предыдущее значение в качестве последней секунды # рендеринга кадров)

Ответ 9

Как я это делаю!

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". После первого тика он делает переменную "fps" равной количеству тиков галочки, деленному на время минус время первого тика.

Fps является целым числом, поэтому "(int)".

Ответ 10

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));

Ответ 11

Установите счетчик на ноль. Каждый раз, когда вы рисуете рамку, увеличивайте счетчик. После каждой секунды печатайте счетчик. пена, промыть, повторить. Если вы хотите получить дополнительный кредит, сохраните счетчик и разделите его на общее количество секунд для среднего значения.

Ответ 12

хранить время начала и увеличивать ваш фреймвордер один раз за цикл? каждые несколько секунд вы можете просто распечатать framecount/(Now - starttime), а затем повторно инициализировать их.

Изменить: oops. дважды ninja'ed

Ответ 13

В псевдокоде (С++ like) эти два являются тем, что я использовал в промышленных приложениях для обработки изображений, которым приходилось обрабатывать изображения из набора камер, запускаемых извне. Вариации в "частоте кадров" имели другой источник (медленное или более быстрое производство на поясе), но проблема одна и та же. (Я предполагаю, что у вас есть простой вызов timer.peek(), который дает вам что-то вроде nc 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: обновляется каждый кадр, требуется больше памяти и процессора

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
   }
} 

Ответ 14

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;
        }
    }

});

Ответ 15

Вот как я это делаю (на 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
}

Ответ 16

Лучше система, чем использование большого массива старых кадров, - это просто сделать что-то вроде этого:

new_fps = old_fps * 0.99 + new_fps * 0.01

Этот метод использует гораздо меньше памяти, требует гораздо меньше кода и придает большее значение последним кадрам, чем старые фреймы, все еще сглаживая эффекты внезапных изменений частоты кадров.

Ответ 17

Вот полный пример, используя Python (но легко адаптированный к любому языку). Он использует уравнение сглаживания в ответе Мартина, поэтому почти нет накладных расходов памяти, и я выбрал значения, которые сработали для меня (не стесняйтесь играть с константами, чтобы адаптироваться к вашему варианту использования).

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)