【问题标题】:Check FPS in JS? [closed]在JS中检查FPS? [关闭]
【发布时间】:2011-06-14 19:38:26
【问题描述】:

如何检查我的 javascript 的 fps?我正在使用它来循环:

gameloopId = setInterval(gameLoop, 10);

【问题讨论】:

    标签: javascript html canvas


    【解决方案1】:

    我的 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 计数器。这并不完美,因为它会使测试过载。

    【讨论】:

      【解决方案2】:

      gameLoop 中,查看最后一个循环中new Datenew Date 之间的区别(将其存储在一个变量中)。
      换句话说:

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

      thisLoop - lastLoop 是两个循环之间经过的毫秒数。

      【讨论】:

      • 你可以用 Date.now() 替换新的日期吗,同样的事情,但更正确 imo
      • 如何根据实际fps刷新函数gameLoop()?因为在使用 setInterval() 时,我只能传递一次时间间隔的参数。
      • @JacekDziurdzikowski:每次调用setTimeout() 来安排下一帧。
      • @JacekDziurdzikowski 使用requestAnimationFrame 比使用setInterval()setTimeout() 性能更好,因为它会自动将时间调整为fps。
      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        我用这个来计算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');
            }
          }
        

        【讨论】:

          【解决方案5】:

          @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 使用上述代码对“游戏”循环进行实际基准测试。

          【讨论】:

          • 非常漂亮的图形!你是怎么做到的?
          • @BennyNeugebauer 以上是在 Excel 中创建的,只是因为一次性使用它比 using HTML5 Canvas for pretty IIR graphing 稍微容易一些。
          • 在使用请求动画帧时我得到无限 fps 有什么原因吗?我使用 1 的过滤强度来获得最准确的 fps?只会不时发生(无限 fps):jsfiddle.net/CezarisLT/JDdjp/10
          • @CezarisLT Infinite FPS == 一个回调报告与上一个调用相同的时间 (new Date)。您可以通过保释 if (thisLoop==lastLoop) 来保护这一点。但是,请注意,如果您使用的过滤器强度为1,则根本没有使用过滤器的意义。只需使用@SLaks 答案。
          • @Phrogz 我知道我参加聚会有点晚了.. 但即使我使用你建议的防护措施来应对无限 fps 错误。我仍然获得无限的fps?有什么建议吗?
          猜你喜欢
          • 2014-11-08
          • 2022-10-07
          • 1970-01-01
          • 1970-01-01
          • 2014-08-17
          • 1970-01-01
          • 2013-03-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多