web-dev-qa-db-fra.com

Vérifiez FPS dans JS?

Comment pourrais-je vérifier les images par seconde de mon javascript? J'utilise ceci pour faire une boucle:

gameloopId = setInterval(gameLoop, 10);
33
CyanPrime

Dans gameLoop, observez la différence entre new Date et new Date depuis la dernière boucle (enregistrez-la dans une variable).
En d'autres termes:

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

thisLoop - lastLoop est le nombre de millisecondes écoulées entre les deux boucles.

25
SLaks

Le code de @Slaks ne vous donne que le FPS instantané de la dernière image, ce qui peut varier ou être trompeur avec le hoquet. Je préfère utiliser un filtre passe-bas facile à écrire et à calculer pour supprimer les transitoires rapides et afficher une pseudo-moyenne raisonnable des résultats récents:

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

La "demi-vie" de ce filtre (le nombre d'images nécessaires pour passer à mi-chemin de l'ancienne valeur à une nouvelle valeur stable) est filterStrength*Math.log(2) (environ 70% de la force). 

Par exemple, une force de 20 se déplacera à mi-chemin d'un changement instantané dans 14 images, les 3/4 du chemin dans 28 images, 90% du trajet dans 46 images et 99% du trajet dans 92 images. Pour un système fonctionnant à environ 30 images par seconde, un changement soudain et radical des performances sera évident en une seconde, mais «éliminera» les anomalies d'une trame, car elles ne modifieront la valeur que de 5%.

Voici une comparaison visuelle des différentes forces de filtrage pour un jeu de ~ 30 images par seconde ayant un creux momentané de 10 images par seconde, puis une vitesse ultérieure pouvant atteindre 50 images par seconde. Comme vous pouvez le constater, les valeurs de filtre plus basses reflètent plus rapidement de «bons» changements, mais sont également plus sensibles aux hoquets temporaires:
enter image description here

Pour finir, voici un exemple d’utilisation du code ci-dessus pour comparer réellement une boucle "de jeu".

91
Phrogz

Qu'en est-il requestAnimationFrame ?

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

J'utilise ceci pour calculer les 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');
    }
  }
2
sha1962

Mes 2 centimes:

Utile pour moi de comparer les optimisations. Gravez un peu de ressources, bien sûr, uniquement à des fins de test.

Idéalement, la cadence de votre application devrait rester supérieure à 50 images par seconde, à la pleine utilisation, lorsque vous utilisez des événements, des boucles, etc. 

Les écrans n'actualiseront pas au-delà de 60h, de toute façon, du moins pour le moment.

Les humains se sentent en retard de moins de 24 FPS, c’est 1000/24 ​​= 41ms

Donc, 41 ms pour un cadre est le dernier point le plus bas critique, avant que les sentiments ne gèlent.

let be = Date.now(),fps=0;
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
    }
 )
<span id="kFps"></span>
<progress id="kpFps" value="0" min="0" max="100" style="vertical-align:middle"></progress>

Juste une boucle pour avoir l’idée, un intervalle de 50ms devrait se dérouler sans encombre! Voir la barre de progression sauter? Ce sont des pertes de cadres, et les navigateurs essayant de suivre par sacrifice, en sautant au cadre suivant, nous devons éviter cela! 

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

0
Cryptopat