calculer FPS dans Canvas en utilisant requestAnimationFrame
Comment puis-je calculer le FPS d'une application de jeu canvas? J'ai vu quelques exemples, mais aucun d'entre eux n'utilise requestAnimationFrame, et je ne sais pas comment appliquer leurs solutions là-bas. C'est mon code:
Au fait, y a-t-il une bibliothèque que je pourrais ajouter pour surperviser les performances?
7 réponses
Ne pas utiliser new Date()
Cette API a plusieurs défauts et n'est utile que pour obtenir la date et l'heure actuelles. Pas de mesure de timespans.
L'API Date utilise l'horloge interne du système d'exploitation, qui est constamment mise à jour et synchronisée avec les serveurs de temps NTP. Cela signifie, que la vitesse / fréquence de cette horloge est parfois plus rapide et parfois plus lent que le temps réel - et donc pas utilisable pour mesurer les durées et framerates.
Si quelqu'un change l'heure du système (manuellement ou en raison de L'heure D'été), vous pourriez au moins voir le problème si une seule image avait soudainement besoin d'une heure. Ou un temps négatif. Mais si l'horloge système coche 20% plus rapidement pour se synchroniser avec l'heure du monde, il est pratiquement impossible de détecter.
En outre, L'API Date est très imprécise-souvent beaucoup moins de 1ms. cela le rend particulièrement inutile pour les mesures de framerate, où une image 60Hz nécessite ~17ms.
À la place, utilisez performance.now()
Le L'API de Performance a été spécifiquement conçue pour de tels cas d'utilisation et peut être utilisée de manière équivalente à new Date()
. Il suffit de prendre l'une des autres réponses et de remplacer new Date()
par performance.now()
, et vous êtes prêt à partir.
Sources:
Aussi contrairement à la Date.maintenant(), les valeurs renvoyées par les Performances.maintenant() toujours augmenter à un rythme constant, indépendamment de l'horloge du système (qui peut être ajusté manuellement ou biaisé par un logiciel comme NTP). Sinon, la performance.calendrier.navigationStart + performance.maintenant() va être à peu près égal à ce Jour.maintenant().
Https://developer.mozilla.org/en-US/docs/Web/API/Performance/now
Et pour windows:
[le service de temps] ajuste la fréquence d'horloge locale pour lui permettre de convergez vers le bon moment. Si la différence de temps entre l'horloge locale et le [échantillon de temps précis] est trop grande pour être corrigée en ajustant fréquence d'horloge, le service de temps définit le local horloge à l'heure correcte.
Https://technet.microsoft.com/en-us/library/cc773013 (v=ws.10).aspx
Vous pouvez garder une trace de la dernière fois que requestAnimFrame a été appelé.
var lastCalledTime;
var fps;
function requestAnimFrame() {
if(!lastCalledTime) {
lastCalledTime = Date.now();
fps = 0;
return;
}
delta = (Date.now() - lastCalledTime)/1000;
lastCalledTime = Date.now();
fps = 1/delta;
}
Chrome est doté d'un compteur de fps: https://developer.chrome.com/devtools/docs/rendering-settings
Il suffit D'ouvrir la console de développement ( F12), d'ouvrir le tiroir ( Esc) et d'ajouter l'onglet "Rendu".
Ici, vous pouvez activer la superposition FPS-Meter pour voir le framerate actuel (y compris. un beau graphique), ainsi que la consommation de mémoire GPU.
Solution multi-navigateurs: Vous pouvez obtenir une superposition similaire avec le Bibliothèque JavaScript stat.js: https://github.com/mrdoob/stats.js/
Il fournit également une belle superposition pour le framerate (incl. graphique) et est très facile à utiliser.
Lors de la comparaison des résultats des statistiques.JS et les outils de développement chrome, les deux montrent exactement les mêmes mesures. Donc, vous pouvez faire confiance à cette bibliothèque pour faire la bonne chose.
J'ai une approche différente, car si vous calculez le FPS, vous obtiendrez ce scintillement en retournant le nombre. J'ai décidé de compter chaque image et de la retourner une fois par seconde
window.countFPS = (function () {
var lastLoop = (new Date()).getMilliseconds();
var count = 1;
var fps = 0;
return function () {
var currentLoop = (new Date()).getMilliseconds();
if (lastLoop > currentLoop) {
fps = count;
count = 1;
} else {
count += 1;
}
lastLoop = currentLoop;
return fps;
};
}());
requestAnimationFrame(function () {
console.log(countFPS());
});
Il suffit de vérifier la différence de temps entre les AFR-callbacks. AFR passe déjà le temps comme argument au rappel. J'ai mis à jour votre violon pour le Montrer: http://jsfiddle.net/WCKhH/1/
Juste une preuve de concept. Code très simple. Tout ce que nous faisons est de définir nos images par seconde et des intervalles entre chaque image. Dans la fonction de dessin, nous déduisons le temps d'exécution de notre dernière image de l'heure actuelle pour vérifier si le temps écoulé depuis la dernière image est supérieur à notre intervalle (qui est basé sur le fps) ou non. Si la condition est évaluée à true, nous définissons l'Heure de notre image actuelle qui sera la "dernière heure d'exécution de l'image" dans le dessin suivant appeler.
var GameLoop = function(fn, fps){
var now;
var delta;
var interval;
var then = new Date().getTime();
var frames;
var oldtime = 0;
return (function loop(time){
requestAnimationFrame(loop);
interval = 1000 / (this.fps || fps || 60);
now = new Date().getTime();
delta = now - then;
if (delta > interval) {
// update time stuffs
then = now - (delta % interval);
// calculate the frames per second
frames = 1000 / (time - oldtime)
oldtime = time;
// call the fn
// and pass current fps to it
fn(frames);
}
}(0));
};
Utilisation:
var set;
document.onclick = function(){
set = true;
};
GameLoop(function(fps){
if(set) this.fps = 30;
console.log(fps);
}, 5);
Voici une autre solution:
var times = [];
var fps;
function refreshLoop() {
window.requestAnimationFrame(function() {
const now = performance.now();
while (times.length > 0 && times[0] <= now - 1000) {
times.shift();
}
times.push(now);
fps = times.length;
refreshLoop();
});
}
refreshLoop();
Cela améliore certains des autres de la manière suivante:
-
performance.now()
est utilisé surDate.now()
pour plus de précision (abordés dans cette réponse) - FPS est mesuré au cours de la dernière seconde afin que le nombre ne saute pas de manière aussi erratique, en particulier pour les applications qui ont des images longues simples.
J'ai écrit sur cette solution plus en détail sur mon site web .