【发布时间】:2011-06-14 19:38:26
【问题描述】:
如何检查我的 javascript 的 fps?我正在使用它来循环:
gameloopId = setInterval(gameLoop, 10);
【问题讨论】:
标签: javascript html canvas
如何检查我的 javascript 的 fps?我正在使用它来循环:
gameloopId = setInterval(gameLoop, 10);
【问题讨论】:
标签: javascript html canvas
我的 2 美分:
对我比较优化很有用。 烧一点资源,当然只是为了测试。
理想情况下,在使用事件、循环等时,您的应用程序帧速率应始终保持在 50 毫秒以下,完全使用时。这等于 20FPS。
人眼感觉滞后于 24 FPS,这是 1000 / 24 = 41ms
所以,41ms 为一帧是最小的时间窗口,以保持自然流动性。应避免高于此值。
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>
只是一个测试循环来了解这个想法,50ms 的间隔,应该保持流畅!
看到上面跳跃的进度条了吗? ^
那些是帧丢失,浏览器试图通过牺牲来跟上,跳到下一帧。应该避免这些尖峰。
let t
for (let i=0;i<99999;i++){
t = setTimeout(function(){
console.log("I am burning your CPU! " + i)
clearTimeout(t)
},50)
}
最新版本的调试器在录制时在performance 选项卡中有一个 FPS 计数器。这并不完美,因为它会使测试过载。
【讨论】:
在gameLoop 中,查看最后一个循环中new Date 和new Date 之间的区别(将其存储在一个变量中)。
换句话说:
var lastLoop = new Date();
function gameLoop() {
var thisLoop = new Date();
var fps = 1000 / (thisLoop - lastLoop);
lastLoop = thisLoop;
...
}
thisLoop - lastLoop 是两个循环之间经过的毫秒数。
【讨论】:
setTimeout() 来安排下一帧。
requestAnimationFrame 比使用setInterval() 或setTimeout() 性能更好,因为它会自动将时间调整为fps。
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)
}
);
【讨论】:
我用这个来计算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');
}
}
【讨论】:
@Slaks 的代码只为您提供最后一帧的瞬时 FPS,这可能会因打嗝而变化或误导。我更喜欢使用易于编写和计算的低通滤波器来消除快速瞬变并显示最近结果的合理伪平均值:
// 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);
此过滤器的“半衰期”(从旧值中途移动到新的稳定值所需的帧数)为 filterStrength*Math.log(2)(大约是强度的 70%)。
例如,20 的强度将在 14 帧中移动到一半,在 28 帧中达到 3/4,在 46 帧中达到 90%,在 99% 中移动有92帧。对于以大约 30fps 运行的系统,性能的突然、剧烈变化将在半秒内显而易见,但仍会“丢弃”单帧异常,因为它们只会将值改变 5% 的差异。
以下是对 30 fps 游戏的不同过滤强度的直观比较,该游戏会瞬间下降到 10 fps,然后加速到 50 fps。如您所见,较低的过滤器值可以更快地反映“良好”的变化,但也更容易受到暂时的干扰:
最后,an example 使用上述代码对“游戏”循环进行实际基准测试。
【讨论】:
new Date)。您可以通过保释 if (thisLoop==lastLoop) 来保护这一点。但是,请注意,如果您使用的过滤器强度为1,则根本没有使用过滤器的意义。只需使用@SLaks 答案。