【问题标题】:HTML5 Canvas performance - calculating loops/frames per secondHTML5 Canvas 性能 - 每秒计算循环数/帧数
【发布时间】:2011-02-22 13:45:03
【问题描述】:

我知道以前有人问过类似这样的问题,例如:Check FPS in JS? - 在某种程度上确实有效,我能够找出每个循环完成的时间。

我正在寻找的是更具可读性和可控性的东西。我希望能够设置 FPS 计数器的刷新率,使其变慢,以便人类可读或尽可能快地运行应用程序,这样我就可以在某种速度计上使用它。

无论如何,这是我现在拥有的代码:

var lastLoop = new Date().getTime();

function updateStage()
{   
    clearCanvas();
    updateStageObjects();
    drawStageObjects();     

    var thisLoop = new Date().getTime(); 
    var fps = (thisLoop - lastLoop);

    $('#details').html(fps);

    lastLoop = thisLoop;
    iteration = setTimeout(updateStage, 1);
}
  1. 将 setTimeout 函数设置为 1 毫秒的速度是否正确?我在想这只会让它尽可能快地循环。

  2. 我是否应该每 100 帧左右计数一次,找出运行 100 帧需要多少毫秒,然后计算如果毫秒为 1000 会计算多少帧?这个计算是什么?

  3. 为了使结果更准确,我猜我需要显示平均值,因为一帧可能会有很大的变化,我应该怎么做?

非常感谢任何提示。

谢谢。

【问题讨论】:

    标签: javascript html canvas frame-rate


    【解决方案1】:
    1. 请注意,更新输出的速度越快,对测量的影响就越大。虽然很少,但我尝试每秒更新一次或更少的 fps 输出,除非需要更快。

    2. 我喜欢对我的结果使用低通滤波器,这样临时的小问题就不会对值产生太大影响。这比移动平均值更容易计算和写入,并且没有整体平均值的问题,即您的“当前”读数会受到整个运行期间的总体性能的影响(例如启动期间的异常读数)。

    总的来说,这是我通常测量 FPS 的方法:

    var fps = 0, now, lastUpdate = (new Date)*1;
    
    // The higher this value, the less the FPS will be affected by quick changes
    // Setting this to 1 will show you the FPS of the last sampled frame only
    var fpsFilter = 50;
    
    function drawFrame(){
      // ... draw the frame ...
    
      var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
      if (now!=lastUpdate){
        fps += (thisFrameFPS - fps) / fpsFilter;
        lastUpdate = now;
      }
    
      setTimeout( drawFrame, 1 );
    }
    
    var fpsOut = document.getElementById('fps');
    setInterval(function(){
      fpsOut.innerHTML = fps.toFixed(1) + "fps";
    }, 1000); 
    

    【讨论】:

    • 嘿,感谢您的回答,这一切似乎都说得通。但由于某种原因,我的输出是 NaN,我已将修改后的代码粘贴到此处,以便您查看:jsfiddle.net/ksgSg
    • @Henryz 多么尴尬,如果 now == lastUpdate 在第一遍时就会发生这种情况。为此,我已经用警卫对其进行了编辑。 (我猜我第一次调用 drawFrame 时通常有一个 setTimeout。)
    • 这似乎效果更好,但如果运行 5 - 10 秒,它有时仍等同于 NaN。我不知道为什么,我还没有完全弄清楚。
    • 如果 fpsFilter 设置为较低的数字,如 10,则不会发生 NaN 问题。所以我让它工作到那个程度。如果您有时间,请给我一个快速解释这是如何工作的:(fps += (thisFrameFPS - fps) / fpsFilter;)。我完全理解其余的,我只是无法弄清楚为什么会产生准确的结果。谢谢!
    • 我想不出代码应该给你NaN的任何理由,而且你选择的因素可能会影响这一点的原因甚至比零还少。你能粘贴你的实际代码吗?无论如何,它所做的就是通过它与当前帧速率之间的差异来慢慢调整fps。如果您的因子为 1,则公式为 fps = fps + thisFrameFPS - fps,它会将其设置为 thisFrameFPS。如果您的因子为 2,并且帧速率从 10 的稳定速率跃升至 20 的稳定速率,那么首先它将 fps 添加 5,然后是 2.5,然后是 1.25,依此类推。
    【解决方案2】:

    我试过了,

    如果你改变了

    lastUpdate = now
    

    lastUpdate = now * 1 - 1;
    

    您的 NaN 问题已解决!这也用于定义 lastUpdate 的地方。可能是因为它无法将日期转换为 unix 时间戳。

    新结果将是:

    var fps = 0, now, lastUpdate = (new Date)*1 - 1;
    
    // The higher this value, the less the FPS will be affected by quick changes
    // Setting this to 1 will show you the FPS of the last sampled frame only
    var fpsFilter = 50;
    
    function drawFrame(){
      // ... draw the frame ...
    
      var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
      fps += (thisFrameFPS - fps) / fpsFilter;
      lastUpdate = now * 1 - 1;
    
      setTimeout( drawFrame, 1 );
    }
    
    var fpsOut = document.getElementById('fps');
    setInterval(function(){
      fpsOut.innerHTML = fps.toFixed(1) + "fps";
    }, 1000); 
    

    【讨论】:

      【解决方案3】:

      我采用了已发布的解决方案并对其进行了一些改进。看看这里 - http://jsfiddle.net/ync3S/

      1. 我通过使用 Date.now() 修复了 NaN 错误,而不是每次都构造一个新的日期对象并尝试引用它。这也避免了一些垃圾收集的必要性。
      2. 我稍微整理了一下变量和函数名称,并添加了一些额外的注释 - 不是必需的,但很高兴。
      3. 我包含了一些用于测试的绘图代码。
      4. 我添加了 fpsDesired 作为引擎循环的测试变量。
      5. 我从 fpsDesired 开始 fpsAverage,因此使用 fpsFilter 时,它不能从 0 上升到真正的 FPS,而是从所需的 FPS 开始并从那里调整。
      6. 现在绘制块以防它已经在绘制,这可用于暂停和其他控制功能。

      主要块如下:

      var fpsFilter = 1; // the low pass filter to apply to the FPS average
      var fpsDesired = 25; // your desired FPS, also works as a max
      var fpsAverage = fpsDesired;
      var timeCurrent, timeLast = Date.now();
      var drawing = false;
      
      function fpsUpdate() {
          fpsOutput.innerHTML = fpsAverage.toFixed(2);
      }
      
      function frameDraw() {
          if(drawing) { return; } else { drawing = true; }
      
          timeCurrent = Date.now();
          var fpsThisFrame = 1000 / (timeCurrent - timeLast);
          if(timeCurrent > timeLast) {
              fpsAverage += (fpsThisFrame - fpsAverage) / fpsFilter;
              timeLast = timeCurrent;
          }
      
          drawing = false;
      }
      
      setInterval(fpsUpdate, 1000);
      fpsUpdate();
      
      setInterval(frameDraw, 1000 / fpsDesired);
      frameDraw();
      

      打算找个修补匠,看看我能不能想出一些更流畅的东西,因为这个帖子在 Google 搜索结果中接近顶部。

      让我们看看我们作为一个团队都能想出什么,我认为不使用 3rd 方库总是很好的,让代码对任何人都可移植:)

      -普拉提玛

      【讨论】:

        【解决方案4】:

        只需设置一个每秒重置 fps 计数器的时间间隔。

        var fpsOut, fpsCount;
        
        var draw = function () {
        
            fpsCount++;
        
            ..Draw To Canvas..
        
        
            ..Get the fps value: fpsOut
        
            requestAnimationFrame(draw);
        
        };
        setInterval(function () {
        
            fpsOut = fpsCount;
            fpsCount = 0;
        
        }, 1000);
        
        draw();
        

        【讨论】:

          【解决方案5】:

          如果您想要实时更新,请考虑让它一次又一次地实时循环。为了减少对性能的影响,只更新受控变量,在本例中为 FPS。你可以有可选的帧延迟,我会放在这里,以防万一。只需根据需要复制、粘贴和调整代码即可。

          请注意,单帧持续 16.66 毫秒。

          setInterval(function(){var latencybase1 = parseFloat(new Date().getTime());
          var latencybase2 = parseFloat(new Date().getTime());
          var latency = latencybase2-latencybase1;
          var fps = Math.round(1000/latency);
          
          if (latency<16.66) 
          {document.getElementById("FPS").innerHTML = fps+" 
          FPS";}
          else {document.getElementById("FPS").innerHTML = ""+fps+" FPS";}
          document.getElementById("Latency").innerHTML = latency+" ms";}, 0);
          

          【讨论】:

          • 我可能误读了,但似乎这段代码只测量调用第二个new Date().getTime()) 所花费的时间,仅此而已。另请注意,60Hz 并不是唯一的刷新率,setInterval 在第 5 次迭代后至少有 4 毫秒的延迟,并且在每个渲染帧中多次设置 innerHTML 只会白白杀死树木。
          猜你喜欢
          • 1970-01-01
          • 2014-06-18
          • 1970-01-01
          • 2019-09-30
          • 2019-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多