답변:
평활화 된 평균이 필요합니다. 가장 쉬운 방법은 현재 답변 (마지막 프레임을 그리는 데 걸리는 시간)을 가져와 이전 답변과 결합하는 것입니다.
// eg.
float smoothing = 0.9; // larger=more smoothing
measurement = (measurement * smoothing) + (current * (1.0-smoothing))
0.9 / 0.1 비율을 조정하여 '시정 수'를 변경할 수 있습니다. 즉, 숫자가 변화에 얼마나 빠르게 반응 하는지를 나타냅니다. 이전 답변을 선호하는 분수가 클수록 변경이 더 느리게 이루어지고 새 답변을 선호하는 분수가 클수록 값이 더 빠르게 변경됩니다. 분명히 두 가지 요소가 하나에 더해져야합니다!
last_frame
하지 않습니다 (또는 적어도 의미 해서는 안 됨 ). time
마지막 프레임에 대해 계산 한 값을 의미해야합니다 . 이렇게하면 모든 이전 프레임이 포함되고 가장 최근 프레임에 가장 많은 가중치가 적용됩니다.
이것이 제가 많은 게임에서 사용한 것입니다.
#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);
}
tickindex = (tickindex + 1) % MAXSAMPLES;
음, 확실히
frames / sec = 1 / (sec / frame)
그러나 지적했듯이 단일 프레임을 렌더링하는 데 걸리는 시간에는 많은 차이가 있으며 UI 관점에서 프레임 속도로 fps 값을 업데이트하는 것은 전혀 사용할 수 없습니다 (숫자가 매우 안정적이지 않은 경우).
당신이 원하는 것은 아마도 이동 평균이나 일종의 비닝 / 리셋 카운터 일 것입니다.
예를 들어, 마지막 30, 60, 100 또는 What-have-you 프레임 각각에 대한 렌더링 시간을 보유한 큐 데이터 구조를 유지할 수 있습니다 (런타임에 제한을 조정할 수 있도록 설계 할 수도 있음). 적절한 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으로 재설정하고 타임 스탬프를 현재 시간으로 설정합니다.
이를 수행하는 방법은 최소한 두 가지입니다.
첫 번째는 다른 사람들이 여기서 언급 한 것입니다. 가장 간단하고 선호하는 방법이라고 생각합니다. 당신은 단지 추적하기 위해
이 경우 fps를 계산하는 것은 다음 공식을 평가하는 것만 큼 간단합니다.
그런 다음 언젠가 사용하고 싶은 멋진 방법이 있습니다.
고려해야 할 'i'프레임이 있다고 가정 해 보겠습니다. 이 표기법을 사용할 것입니다 : 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])
이제 여기서 트릭은 공식 (2)의 오른쪽을 포함하고 왼쪽을 대체하는 방식으로 공식 (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)
따라서이 공식에 따라 (내 수학 도출 기술은 약간 녹슬 었습니다), 새로운 fps를 계산하려면 이전 프레임의 fps, 마지막 프레임을 렌더링하는 데 걸린 시간 및 사용한 프레임 수를 알아야합니다. 렌더링.
이것은 대부분의 사람들에게 과도한 것일 수 있으므로 구현할 때 게시하지 않은 것입니다. 그러나 매우 견고하고 유연합니다.
마지막 프레임 시간과 함께 대기열을 저장하므로 마지막 프레임을 고려하는 것보다 훨씬 더 정확하게 평균 FPS 값을 계산할 수 있습니다.
또한 인위적으로 프레임의 시간을 망칠 것이라는 것을 알고있는 경우 프레임 하나를 무시할 수 있습니다.
또한 실행되는 동안 대기열에 저장할 프레임 수를 변경할 수 있으므로 자신에게 가장 적합한 값을 즉시 테스트 할 수 있습니다.
// 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);
}
여기에 좋은 답변이 있습니다. 그것을 구현하는 방법은 필요한 것에 달려 있습니다. 나는 위의 사람이 "time = time * 0.9 + last_frame * 0.1"러닝 평균을 선호합니다.
그러나 저는 개인적으로 새로운 데이터에 대한 평균 가중치를 더 높이고 싶습니다. 게임에서 SPIKES가 가장 어렵 기 때문에 저에게 가장 관심이 많기 때문입니다. 따라서 .7 \ .3 분할과 같은 것을 사용하면 스파이크가 훨씬 더 빨리 표시됩니다 (효과가 화면에서 더 빨리 떨어집니다. 아래 참조).
RENDERING 시간에 초점을 맞추고 있다면 .9.1 분할이 꽤 잘 작동합니다. b / c가 더 부드럽습니다. 게임 플레이 / AI / 물리 스파이크의 경우 일반적으로 게임이 고르지 않게 보이기 때문에 훨씬 더 큰 문제가됩니다 (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를 채우십시오.) 그러면 프레임이 끝날 때 FPS 문제 를 찾아 해결할 수 있습니다 .
time = time * 0.9 + last_frame * 0.1
디스플레이를 부드럽게 바꾸는 평균 계산이 마음에 듭니다.
// 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));
다음은 Python을 사용한 완전한 예입니다 (그러나 모든 언어에 쉽게 적용됨). Martin의 대답에서 평활 방정식을 사용하므로 메모리 오버 헤드가 거의 없으며 저에게 적합한 값을 선택했습니다 (사용 사례에 맞게 상수를 자유롭게 사용하십시오).
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)
(c ++ 유사) 의사 코드에서이 두 가지는 외부에서 트리거 된 카메라 세트의 이미지를 처리해야하는 산업용 이미지 처리 응용 프로그램에서 사용한 것입니다. "프레임 속도"의 변형은 소스가 다르지만 (벨트에서 더 느리거나 더 빠른 생산) 문제는 동일합니다. (애플리케이션 시작 또는 마지막 호출 이후 msec (nsec?)의 nr과 같은 것을 제공하는 간단한 timer.peek () 호출이 있다고 가정합니다)
솔루션 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
}
}
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);
}
즉, 틱 시계는 틱을 추적합니다. 처음이라면 현재 시간을 취해 'tickstart'에 넣습니다. 첫 번째 틱 후에 변수 'fps'는 틱 클럭의 틱 수를 시간으로 나눈 값에서 첫 번째 틱 시간을 뺀 값과 같습니다.
Fps는 정수이므로 "(int)"입니다.
수행 방법은 다음과 같습니다 (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
}
Typescript에서는이 알고리즘을 사용하여 프레임 속도와 프레임 시간 평균을 계산합니다.
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';
팁 : 샘플이 1이면 결과는 실시간 프레임 속도와 프레임 시간입니다.
이는 KPexEA의 답변을 기반으로하며 단순 이동 평균을 제공합니다. 간편한 복사 및 붙여 넣기를 위해 정리 및 TypeScript로 변환 :
변수 선언 :
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)