2008-09-17 19 views
97

किसी गेम में फ़्रेम प्रति सेकेंड की गणना करने के लिए एक अच्छा एल्गोरिदम क्या है? मैं इसे स्क्रीन के कोने में एक संख्या के रूप में दिखाना चाहता हूं। अगर मैं सिर्फ अंतिम फ्रेम प्रस्तुत करने में कितना समय लगाता हूं तो संख्या बहुत तेज़ी से बदल जाती है।खेल में प्रति सेकंड फ्रेम की गणना

बोनस पॉइंट्स यदि आपका उत्तर प्रत्येक फ्रेम को अपडेट करता है और फ्रेम दर बढ़ने के दौरान अलग-अलग रूप से अभिसरण नहीं करता है।

उत्तर

88

आपको एक चिकना औसत की आवश्यकता है, वर्तमान उत्तर (अंतिम फ्रेम खींचने का समय) लेने का सबसे आसान तरीका है और इसे पिछले उत्तर के साथ जोड़ना है।

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

0.9/0.1 अनुपात समायोजित करके आप 'समय निरंतर' बदल सकते हैं - यह है कि संख्या कितनी जल्दी परिवर्तनों का जवाब देती है। पुराने उत्तर के पक्ष में एक बड़ा अंश धीमी चिकनी परिवर्तन देता है, नए उत्तर के पक्ष में एक बड़ा अंश एक त्वरित बदलता मूल्य देता है। जाहिर है कि दो कारकों को एक में जोड़ना चाहिए!

+13

फिर मूर्खतापूर्णता और साफ-सफाई के लिए, शायद आप फ्लोट वजन Ratio = 0.1; और समय = समय * (1.0 - weightRatio) + last_frame * weightRatio – korona

+1

सिद्धांत में अच्छा और सरल लगता है, लेकिन हकीकत में इस दृष्टिकोण की चिकनाई शायद ही ध्यान देने योग्य है। अच्छा नहीं। – Petrucio

+1

@ पेट्रूसियो यदि चिकनाई बहुत कम है, तो बस समय निरंतर क्रैंक करें (weightRatio = 0.05, 0.02, 0.01 ...) –

11

प्रत्येक बार जब आप स्क्रीन प्रस्तुत करते हैं तो काउंटर बढ़ाएं और उस समय के अंतराल के लिए काउंटर को साफ़ करें जिस पर आप फ्रेम दर को मापना चाहते हैं।

आईई। प्रत्येक 3 सेकंड, काउंटर/3 प्राप्त करें और फिर काउंटर साफ़ करें।

+0

+1 हालांकि यह आपको अंतराल में केवल एक नया मूल्य देगा, यह समझना आसान है और न ही सरणी की आवश्यकता है और न ही मूल्यों का अनुमान लगाने और वैज्ञानिक रूप से सही है। – opatut

+1

लेकिन फिर आपको एक एफपीएस काउंटर मिला केवल हर 3 सेकंड में ताज़ा किया गया ... – tigrou

0

शून्य पर काउंटर सेट करें। हर बार जब आप एक फ्रेम वृद्धि काउंटर खींचते हैं। काउंटर के प्रत्येक दूसरे प्रिंट के बाद। पाउडर, कुल्ला, दोहराना। यदि आप अतिरिक्त क्रेडिट चाहते हैं, तो रनिंग औसत के लिए सेकंड की कुल संख्या से चलने वाले काउंटर को रखें और विभाजित करें।

1

आप एक काउंटर रख सकता है, के बाद प्रत्येक फ्रेम प्रदान की गई है, तो काउंटर रीसेट जब आप एक नया दूसरे पर हैं

0

दुकान एक शुरुआत (गाया फ्रेम के अंतिम क्षण का # के रूप में पिछले मान भंडारण) यह भी वृद्धि प्रति लूप एक बार अपने फ्रेमकॉन्टर को समय और बढ़ाएं? प्रत्येक कुछ सेकंड आप बस फ्रेमकाउंट/(अब - स्टार्टटाइम) प्रिंट कर सकते हैं और फिर उन्हें पुन: प्रारंभ कर सकते हैं।

संपादित करें: ओह। डबल ninja'ed

22

ठीक है, निश्चित रूप से

frames/sec = 1/(sec/frame) 

लेकिन, जैसा कि आप बाहर बिंदु, समय यह एक भी फ्रेम रेंडर करने के लिए ले जाता है में परिवर्तन का एक बहुत कुछ नहीं है, और अद्यतन करने एफपीएस एक यूआई के नजरिए से फ्रेम दर पर मूल्य बिल्कुल उपयोग नहीं किया जा सकता है (जब तक कि संख्या बहुत स्थिर न हो)।

आप जो चाहते हैं वह शायद एक चलती औसत या कुछ प्रकार के बिनिंग/रीसेटिंग काउंटर है।

उदाहरण के लिए, आप एक कतार डेटा संरचना को बनाए रख सकते हैं जिसमें पिछले 30, 60, 100, या आपके पास क्या फ्रेम हैं (आप इसे डिज़ाइन भी कर सकते हैं ताकि सीमा को चलाने पर समायोज्य हो सके -पहर)। निर्धारित करने के लिए एक सभ्य एफपीएस सन्निकटन आप कतार में सभी प्रतिपादन काल से औसत एफपीएस निर्धारित कर सकते हैं:

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

जब आप एक नया फ्रेम प्रतिपादन समाप्त आप एक नया प्रतिपादन समय enqueue और एक पुराने प्रतिपादन समय विपंक्ति। वैकल्पिक रूप से, आप केवल तभी निकाला जा सकता है जब प्रतिपादन समय कुल प्रीसेट मान (उदा। 1 सेकंड) से अधिक हो जाता है। आप "अंतिम एफपीएस मान" और अंतिम अद्यतन टाइमस्टैम्प को बनाए रख सकते हैं ताकि यदि आप चाहें तो एफपीएस आकृति को अपडेट करने के लिए आप ट्रिगर कर सकते हैं। यद्यपि आपके पास लगातार स्वरूपण होने पर चलती औसत के साथ, प्रत्येक फ्रेम पर "तत्काल औसत" एफपीएस प्रिंट करना शायद ठीक होगा।

एक और तरीका रीसेटिंग काउंटर होना होगा। एक सटीक (मिलीसेकंद) टाइमस्टैम्प, एक फ्रेम काउंटर, और एक एफपीएस मूल्य बनाए रखें। जब आप फ्रेम को प्रतिपादित करते हैं, काउंटर बढ़ाते हैं। जब काउंटर एक पूर्व निर्धारित सीमा (जैसे 100 फ्रेम) या जब टाइमस्टैम्प के बाद से समय कुछ पूर्व निर्धारित मान प्रदान किया है (उदाहरण के लिए 1 सेकंड) मारता है, गणना एफपीएस:

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

तब के लिए काउंटर रीसेट 0 और टाइमस्टैम्प को वर्तमान समय पर सेट करें।

42

यह मैंने कई खेलों में उपयोग किया है।

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

मुझे यह दृष्टिकोण बहुत पसंद है। किसी भी विशिष्ट कारण से आप MAXSAMPLES को 100 क्यों सेट करते हैं? – Zolomon

+1

MAXSAMPLES यहां मूल्यों की संख्या है जो एफपीएस के लिए मूल्य के साथ आने के लिए औसत हैं। –

+6

यह आसान चलती औसत (एसएमए) – KindDragon

0

(C++ की तरह) स्यूडोकोड इन दोनों में क्या मैं औद्योगिक छवि प्रसंस्करण अनुप्रयोगों है कि बाहर से शुरू हो रहा कैमरे के का एक सेट से छवियों को संसाधित करने के लिए किया था में इस्तेमाल कर रहे हैं। "फ्रेम दर" में बदलावों का एक अलग स्रोत था (बेल्ट पर धीमा या तेज उत्पादन) लेकिन समस्या एक जैसी है। (मुझे लगता है आप एक सरल timer.peek() कॉल है कि आप आवेदन के बाद से 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 
    } 
} 
2

अच्छा यहाँ जवाब की आवश्यकता है। बस आप इसे कैसे कार्यान्वित करते हैं इस पर निर्भर है कि आपको इसके लिए क्या चाहिए। मैं उपरोक्त लड़के द्वारा खुद को औसत समय "समय = समय * 0.9 + अंतिम_फ्रेम * 0.1" पसंद करता हूं।

हालांकि मैं व्यक्तिगत रूप से अपने डेटा को नए डेटा के प्रति अधिक भारित करना चाहता हूं क्योंकि एक खेल में यह स्पीक है जो स्क्वैश करने के लिए सबसे कठिन है और इस प्रकार मेरे लिए सबसे अधिक रुचि है। तो मैं कुछ और .7 \ .3 स्प्लिट की तरह अधिक उपयोग करता हूं, जो स्पाइक को बहुत तेजी से दिखाएगा (हालांकि इसका प्रभाव तेजी से ऑफ-स्क्रीन भी बंद हो जाएगा .. नीचे देखें)

यदि आपका फ़ोकस रेंडरिंग समय पर है , तो .9.1 विभाजन बहुत अच्छी तरह से काम करता है बी/सी यह अधिक चिकनी होने लगता है। गेमप्ले/एआई/भौतिकी स्पाइक के लिए बहुत अधिक चिंता का विषय है क्योंकि आमतौर पर यह आपके गेम को चॉकलेट (जो कम फ्रेम दर से भी बदतर होता है) मानते हैं कि हम 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 में भरने) इससे आपको यह जानने दूँगी और इस तरह का समाधान एफपीएस के अंत जारी करता है: मैं क्या होगा भी कुछ इस तरह जोड़ने है वे फ्रेम होते हैं।


पहले एक दूसरे मुझे पहले यहाँ उल्लेख किया है:

+0

मुझे 'time = time * 0.9 + last_frame * 0.1' औसत गणना पसंद है जो प्रदर्शन को आसानी से बदल देता है। दूसरी विधि के लिए –

9

वहाँ यह करने के लिए कम से कम दो तरीके हैं। मुझे लगता है कि यह सबसे सरल और पसंदीदा तरीका है।तुम बस

  • cn का ट्रैक रखने के: आप कितने तख्ते गाया गया है
  • TIME_START के काउंटर: समय के बाद से आप की गिनती
  • time_now शुरू कर दिया है: वर्तमान समय

इस मामले में एफपीएस की गणना करना इस सूत्र का मूल्यांकन करने जितना सरल है:

  • FPS = cn/(time_now - time_start)।

    मान लीजिए कि आप 'मैं' तख्ते पर विचार करने के लिए है दो:


तो फिर वहाँ uber अच्छा तरीका है कि आप कुछ दिन का उपयोग करना चाहते हो सकता है। मैं इस नोटेशन का उपयोग करूंगा: f [0], f [1], ..., f [i-1] यह वर्णन करने के लिए कि फ्रेम 0, फ्रेम 1, ..., फ्रेम (i-1 प्रस्तुत करने में कितना समय लगेगा क्रमशः)।

Example where i = 3 

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

फिर, मैं फ्रेम के बाद एफपीएस की गणितीय परिभाषा

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

होगा और एक ही सूत्र है, लेकिन केवल मैं -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) 

तो इस सूत्र के अनुसार (मेरी गणित कौशल पाने के लिए थोड़ा ज़ंग हालांकि रहे हैं), नई गणना करने के लिए एफपीएस आपको पिछले फ्रेम से एफपीएस जानने की जरूरत है, जो अंतिम फ्रेम और आपके द्वारा प्रदान किए गए फ्रेम की संख्या को प्रस्तुत करने के लिए लिया गया अवधि है।

+0

+1। मुझे लगता है कि यह uber सटीक गणना के लिए अच्छा होगा: 3 – zeboidlund

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

}); 
1

मैं यह कैसे करता हूं!

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

शब्दों में, एक टिक घड़ी टिक टिकता है। यदि यह पहली बार है, तो यह वर्तमान समय लेता है और इसे 'टिकस्टार्ट' में रखता है। पहली टिक के बाद, यह वैरिएबल 'एफपीएस' बनाता है, टिक टिक की कितनी टिकें पहली टिक के समय से कम समय तक विभाजित होती हैं।

एफपीएस एक पूर्णांक है, इसलिए "(int)"।

+0

किसी के लिए अनुशंसा नहीं करेंगे। सेकंड की कुल संख्या से टिकों की कुल संख्या को विभाजित करना एफपीएस को गणितीय सीमा की तरह कुछ दृष्टिकोण बनाता है, जहां यह मूल रूप से लंबे समय के बाद 2-3 मानों पर स्थिर होता है, और गलत परिणाम प्रदर्शित करता है। –

0

यहाँ कैसे मैं इसे (जावा में) कर दिया गया है:

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

यह ज्यादातर लोगों के लिए overkill हो सकता है, यही कारण है कि मैं इसे पोस्ट नहीं किया था जब मैं इसे लागू किया है। लेकिन यह बहुत मजबूत और लचीला है।

यह अंतिम फ्रेम समय के साथ एक कतार स्टोर करता है, इसलिए यह अंतिम फ्रेम को ध्यान में रखते हुए औसत एफपीएस मूल्य की सटीक गणना कर सकता है।

यह आपको एक फ्रेम को अनदेखा करने की अनुमति देता है, अगर आप ऐसा कुछ कर रहे हैं जो आपको पता है कि कृत्रिम रूप से उस फ्रेम के समय को खराब कर रहा है।

यह आपको कतार में स्टोर करने के लिए फ्रेम की संख्या को बदलने की अनुमति देता है, इसलिए आप फ्लाई पर इसका परीक्षण कर सकते हैं कि आपके लिए सबसे अच्छा मूल्य क्या है।

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

एक वर्ष फ़्रेमदर की विस्तृत शृंखला का उपयोग कर की तुलना में बेहतर प्रणाली सिर्फ इस तरह कुछ करने के लिए है:

new_fps = old_fps * 0.99 + new_fps * 0.01 

इस विधि अब तक कम स्मृति का उपयोग करता है, अब तक कम कोड की आवश्यकता है, और अधिक महत्व देता है हाल ही में framerates पर पुराने framerates की तुलना में जबकि अचानक framerate परिवर्तन के प्रभाव smoothing।

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

यहां एक संपूर्ण उदाहरण है, अजगर का उपयोग कर (लेकिन आसानी से किसी भी भाषा के लिए अनुकूलित)। यह मार्टिन के जवाब में चिकनाई समीकरण का उपयोग करता है, इसलिए लगभग कोई स्मृति ओवरहेड नहीं है, और मैंने मूल्यों को चुना है जो मेरे लिए काम करते हैं (आपके उपयोग के मामले में अनुकूलन करने के लिए स्थिरांक के साथ खेलने के लिए स्वतंत्र महसूस करें)।

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) 
संबंधित मुद्दे