Wie überprüfe ich die Framerate in Javascript? [closed]

Lesezeit: 6 Minuten

Benutzeravatar von CyanPrime
CyanPrime

Wie würde ich die Framerate in meinem Javascript-Code überprüfen? Ich verwende dies zum Schleifen:

gameloopId = setInterval(gameLoop, 10);

Benutzeravatar von Phrogz
Phrogz

Der Code von @Slaks gibt Ihnen nur die momentanen FPS des letzten Frames, die variieren oder mit Schluckauf irreführend sein können. Ich ziehe es vor, einen einfach zu schreibenden und zu berechnenden Tiefpassfilter zu verwenden, um schnelle Transienten zu entfernen und einen vernünftigen Pseudo-Durchschnitt der letzten Ergebnisse anzuzeigen:

// The higher this value, the less the fps will reflect temporary variations
// A value of 1 will only keep the last value
var filterStrength = 20;
var frameTime = 0, lastLoop = new Date, thisLoop;

function gameLoop(){
  // ...
  var thisFrameTime = (thisLoop=new Date) - lastLoop;
  frameTime+= (thisFrameTime - frameTime) / filterStrength;
  lastLoop = thisLoop;
}

// Report the fps only every second, to only lightly affect measurements
var fpsOut = document.getElementById('fps');
setInterval(function(){
  fpsOut.innerHTML = (1000/frameTime).toFixed(1) + " fps";
},1000);

Die „Halbwertszeit“ dieses Filters – die Anzahl der Frames, die benötigt wird, um sich vom alten Wert auf halbem Weg zu einem neuen, stabilen Wert zu bewegen – ist filterStrength*Math.log(2) (ca. 70 % der Stärke).

Zum Beispiel eine Stärke von 20 bewegt sich auf halbem Weg zu einer sofortigen Änderung in 14 Frames, 3/4 des Weges dorthin in 28 Frames, 90 % des Weges dorthin in 46 Frames und 99 % des Weges dorthin in 92 Frames. Bei einem System, das mit etwa 30 fps läuft, wird eine plötzliche, drastische Leistungsänderung innerhalb einer halben Sekunde offensichtlich sein, aber Einzelbildanomalien werden immer noch „weggeworfen“, da sie den Wert nur um 5 % der Differenz verschieben.

Hier ist ein visueller Vergleich verschiedener Filterstärken für ein Spiel mit ~30 fps, das einen vorübergehenden Einbruch auf 10 fps und später eine Geschwindigkeit von bis zu 50 fps aufweist. Wie Sie sehen können, spiegeln niedrigere Filterwerte schneller „gute“ Änderungen wider, sind aber auch anfälliger für vorübergehende Schluckaufe:
Geben Sie hier die Bildbeschreibung ein

Endlich, hier ist ein Beispiel den obigen Code zu verwenden, um tatsächlich eine ‘Spiel’-Schleife zu bewerten.

  • Sehr schöne Grafik! Wie hast du das gemacht?

    – Benny Code

    13. Februar 2013 um 23:12 Uhr

  • @BennyNeugebauer Das obige wurde in Excel erstellt, nur weil es für eine einmalige Sache etwas einfacher war als Verwenden von HTML5 Canvas für hübsche IIR-Grafiken.

    – Phrogz

    14. Februar 2013 um 3:42 Uhr

  • Gibt es einen Grund, warum ich unendliche fps erhalte, wenn ich den Anforderungsanimationsrahmen verwende? Ich verwende eine Filterstärke von 1 für die genauesten fps? passiert nur ab und zu (unendlich fps) : jsfiddle.net/CezarisLT/JDdjp/10

    – Kivylios

    17. Dezember 2013 um 22:23 Uhr

  • @CezarisLT Infinite FPS == ein Rückruf, der zur gleichen Zeit gemeldet wird (new Date) wie beim vorherigen Aufruf. Sie können dies durch Kaution verhindern if (thisLoop==lastLoop). Beachten Sie jedoch, dass, wenn Sie eine Filterstärke von verwenden 1, es macht überhaupt keinen Sinn, den Filter zu verwenden. Verwenden Sie einfach @SLaks Antwort.

    – Phrogz

    19. Dezember 2013 um 1:49 Uhr

  • @Phrogz Ich weiß, ich bin ein bisschen spät zur Party … aber selbst wenn ich Ihren vorgeschlagenen Schutz gegen den unendlichen FPS-Fehler verwende. Ich bekomme immer noch unendlich fps? irgendwelche Vorschläge?

    – FutureCake

    15. November 2017 um 22:47 Uhr


Benutzeravatar von SLaks
SLaks

In gameLoopsehen Sie sich den Unterschied zwischen an new Date Und new Date aus der letzten Schleife (in einer Variablen speichern).
Mit anderen Worten:

var lastLoop = new Date();
function gameLoop() { 
    var thisLoop = new Date();
    var fps = 1000 / (thisLoop - lastLoop);
    lastLoop = thisLoop;
    ...
}

thisLoop - lastLoop ist die Anzahl der Millisekunden, die zwischen den beiden Schleifen vergangen sind.

  • könntest du new Date durch Date.now() ersetzen, dasselbe, aber korrekter imo

    – caub

    10. August 2016 um 17:16 Uhr

  • Wie aktualisiere ich die Funktion gameLoop() in Abhängigkeit von den tatsächlichen fps? Denn bei Verwendung von setInterval() kann ich das Argument des Zeitintervalls nur einmal übergeben.

    – Jacek Dziurdzikowski

    25. Februar 2018 um 11:35 Uhr


  • @JacekDziurdzikowski: Anruf setTimeout() jedes Mal, um den nächsten Rahmen zu planen.

    – SLaks

    25. Februar 2018 um 15:38 Uhr


  • @JacekDziurdzikowski Verwenden requestAnimationFrame ist besser für die Leistung als beides zu verwenden setInterval() oder setTimeout() da es das Timing automatisch an die fps anpasst.

    – Greyson R.

    27. Juli 2019 um 23:49 Uhr

  • Wenn es nach mir ginge, würde ich aufgrund der Vorteile in Genauigkeit und Effizienz lieber so etwas wie performance.now anstelle von Datumsangaben verwenden

    – AlphaHeul

    12. November 2022 um 11:08 Uhr

Benutzeravatar von NVRM
NVRM

Meine 2 Cent:

Nützlich für mich, um Optimierungen zu vergleichen. Verbrennen Sie natürlich ein bisschen Ressourcen, nur zum Testen.

Im Idealfall sollte Ihre App-Bildrate bei voller Nutzung, bei Verwendung von Ereignissen, Schleifen usw. immer deutlich unter 50 ms pro Bild bleiben. Dies entspricht 20 FPS.

Das menschliche Auge fühlt sich unter 24 FPS verzögert, das ist 1000/24 ​​= 41 ms

So, 41ms denn ein Rahmen ist das kleinste Zeitfenster, um die natürliche Fließfähigkeit zu erhalten. Höhere Werte sind zu vermeiden.

let be = Date.now(),fps=0,info='';
requestAnimationFrame(
    function loop(){
        let now = Date.now()
        fps = Math.round(1000 / (now - be))
        be = now
        requestAnimationFrame(loop)
        if (fps < 35){
          kFps.style.color = "red"
          kFps.textContent = fps 
        } if (fps >= 35 && fps <= 41) {
            kFps.style.color = "deepskyblue"
            kFps.textContent = fps + " FPS"
          } else {
            kFps.style.color = "black"
            kFps.textContent = fps + " FPS"
        }
        kpFps.value = fps;
        info+=(''+new Date()+' '+fps+'\n');
    }
 )
<span id="kFps"></span>
<progress id="kpFps" value="0" min="0" max="100" style="vertical-align:middle"></progress>
<button onclick="try{console.clear();console.info(info)}catch{}">Statistics</button>

Nur eine Testschleife, um die Idee zu bekommen, 50-ms-Intervall, sollte reibungslos mithalten!

Sehen Sie den Fortschrittsbalken über dem Springen? ^

Das sind Frame-Verluste, die der Browser versucht, durch Opfer mitzuhalten, indem er zum nächsten Frame springt. Diese Spitzen sind zu vermeiden. Der nächste Ausschnitt verbrennt Ressourcen (dh FPS):

let t
for (let i=0;i<99999;i++){
  t = setTimeout(function(){
   console.log("I am burning your CPU! " + i)
   clearTimeout
  },50)
  
}

Neuere Versionen von Debuggern haben einen FPS-Zähler, in dem performance Registerkarte, während der Aufnahme. Dies ist nicht perfekt, da es die Tests überlastet.

Geben Sie hier die Bildbeschreibung ein

Wie wäre es mit AnfrageAnimationFrame?

var before,now,fps;
before=Date.now();
fps=0;
requestAnimationFrame(
    function loop(){
        now=Date.now();
        fps=Math.round(1000/(now-before));
        before=now;
        requestAnimationFrame(loop);
        console.log("fps",fps)
    }
 );

Damit berechne ich die fps

  var GameCanvas = document.getElementById("gameCanvas");
  var GameContext = doContext(GameCanvas,"GameCanvas");
  var FPS = 0;
  var TimeNow;
  var TimeTaken;
  var ASecond = 1000;
  var FPSLimit = 25;
  var StartTime = Date.now();
  var TimeBefore = StartTime;
  var FrameTime = ASecond/FPSLimit;
  var State = { Title:0, Started:1, Paused:2, Over:3 };
  var GameState = State.Title;

  function gameLoop() {
    requestAnimationFrame(gameLoop);
    TimeNow = Date.now();
    TimeTaken = TimeNow - TimeBefore;

    if (TimeTaken >= FrameTime) {
      FPS++
      if((TimeNow - StartTime) >= ASecond){
        StartTime += ASecond;
        doFPS();
        FPS = 0;
      }

      switch(GameState){
        case State.Title :
          break;
        case State.Started :
          break;
        case State.Paused :
          break;
        case State.Over :
          break;
      }
      TimeBefore = TimeNow - (TimeTaken % FrameTime);
    }
  }

  Sprites.onload = function(){
    requestAnimationFrame(gameLoop);
  }

  function drawText(Context,_Color, _X, _Y, _Text, _Size){
    Context.font =  "italic "+ _Size +" bold";
    Context.fillStyle = _Color;
    Context.fillText(_Text, _X, _Y);
  }

  function doFPS()(
    drawText(GameContext,"black",10,24,"FPS : " + FPS,"24px");
  }

  function doContext(Canvas,Name){
    if (Canvas.getContext) {
      var Context = Canvas.getContext('2d');
      return Context;
    }else{
      alert( Name + ' not supported your Browser needs updating');
    }
  }

1449100cookie-checkWie überprüfe ich die Framerate in Javascript? [closed]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy