【问题标题】:Recording FPS in webGL在 webGL 中记录 FPS
【发布时间】:2018-04-19 18:40:43
【问题描述】:

我正在尝试比较移动设备上 3d 应用程序的性能。我在 webGL 中设置了一个 3d 太阳能系统,我试图记录或至少显示 FPS。到目前为止,这就是我所拥有的:

在体内

<script language="javascript">
var x, message;
x = Time;
message = "fps is equal to ";
document.write (message); // prints the value of the message variable
document.write (x); //prints the value of x
</script>

为了在画布的绘制功能中获得 Time Var,我有这个

var Time = 0;
function drawScene() {
var startTime = new Date();
//draw scene here
var endTime = new Date();
Time = (endTime - startTime)
}

我在画布底部得到的输出是“fps 等于 null”

任何帮助都会很棒!

【问题讨论】:

标签: javascript webgl


【解决方案1】:

显示 FPS 非常简单,除了通常想知道之外,与 WebGL 没有任何关系。这是一个小的 FPS 显示

const fpsElem = document.querySelector("#fps");

let then = 0;
function render(now) {
  now *= 0.001;                          // convert to seconds
  const deltaTime = now - then;          // compute time since last frame
  then = now;                            // remember time for next frame
  const fps = 1 / deltaTime;             // compute frames per second
  fpsElem.textContent = fps.toFixed(1);  // update fps display
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
&lt;div&gt;fps: &lt;span id="fps"&gt;&lt;/span&gt;&lt;/div&gt;

将 requestAnimationFrame 用于动画,因为这就是它的用途。浏览器可以同步到屏幕刷新,为您提供黄油般流畅的动画。如果您的页面不可见,他们也可以停止处理。另一方面 setTimeout 不是为动画设计的,不会同步到浏览器的页面绘制。

您可能不应该使用Date.now() 来计算FPS,因为Date.now() 只返回毫秒。同样使用(new Date()).getTime() 尤其糟糕,因为它每帧都会生成一个新的Date 对象。

requestAnimationFrame 自页面加载以来已经经过了以微秒为单位的时间,所以只需使用它。

跨帧平均 FPS 也很常见。

const fpsElem = document.querySelector("#fps");
const avgElem = document.querySelector("#avg");

const frameTimes = [];
let   frameCursor = 0;
let   numFrames = 0;   
const maxFrames = 20;
let   totalFPS = 0;

let then = 0;
function render(now) {
  now *= 0.001;                          // convert to seconds
  const deltaTime = now - then;          // compute time since last frame
  then = now;                            // remember time for next frame
  const fps = 1 / deltaTime;             // compute frames per second
  
  fpsElem.textContent = fps.toFixed(1);  // update fps display
  
  // add the current fps and remove the oldest fps
  totalFPS += fps - (frameTimes[frameCursor] || 0);
  
  // record the newest fps
  frameTimes[frameCursor++] = fps;
  
  // needed so the first N frames, before we have maxFrames, is correct.
  numFrames = Math.max(numFrames, frameCursor);
  
  // wrap the cursor
  frameCursor %= maxFrames;
    
  const averageFPS = totalFPS / numFrames;

  avgElem.textContent = averageFPS.toFixed(1);  // update avg display
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
body { font-family: monospace; }
<div>        fps: <span id="fps"></span></div>
<div>average fps: <span id="avg"></span></div>

【讨论】:

  • 虽然您的大部分答案都很好,但重要的一点不是:将 1 除以帧时间 (deltaTime) 不是计算每秒帧数的正确方法。您应该计算一秒钟内渲染帧的数量,就像在接受的答案中一样。
  • 如果一帧耗时 0.9 秒,而 59 帧耗时 0.00169 秒,如果您所做的只是查看 1 秒内绘制的帧数,您将不会看到这一点。
  • 你当然会看到。应用程序以 0.00169 秒的帧时间以 600 FPS 的速度渲染。因此,如果现在一帧需要 0.9 秒,那么帧计数器将计数 60,而不是 600 帧,因此您会看到 10 倍的差异。帧计数就像你的averageFPS,但变量更少,代码更简单。
  • 这个const fps = 1 / deltaTime; 只是一个一厢情愿的想法,如果所有帧都在那个时候渲染,我们可以拥有多少 FPS。
  • 顺便说一句,WebGL 基础非常棒! :)
【解决方案2】:

我假设您重复调用 drawScene,但如果您只设置一次 x,那么它不会在每次调用 drawScene 时更新。此外,您在 Time 中存储的内容是经过的时间,而不是每秒帧数。

像下面这样的东西怎么样?这个想法是计算渲染的帧数,并在经过一秒后将其存储在 fps 变量中。

<script>
var elapsedTime = 0;
var frameCount = 0;
var lastTime = 0;

function drawScene() {

   // draw scene here

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

   frameCount++;
   elapsedTime += (now - lastTime);

   lastTime = now;

   if(elapsedTime >= 1000) {
       fps = frameCount;
       frameCount = 0;
       elapsedTime -= 1000;

       document.getElementById('test').innerHTML = fps;
   }
}

lastTime = new Date().getTime();
setInterval(drawScene,33);

</script>

<div id="test">
</div>

【讨论】:

  • 永远不要将setInterval 用于动画。它隐藏在选项卡时,它使用用户的计算机。使用requestAnimationFrame。见paulirish.com/2011/requestanimationframe-for-smart-animating
  • 谢谢,这正是我所需要的!
  • 不客气!也请查看@gman 的答案和requestAnimationFrame,因为这是制作动画的更好方法。
  • 不应该elapsedTime -= 1000;elapsedTime = 0; 吗?在我做出改变之前,我无法让它发挥作用。
【解决方案3】:

我创建了 Barış Uşaklı 答案的面向对象版本。 它还跟踪最后一分钟的平均 fps。

用法:

全局变量:

var fpsCounter;

在启动程序时在某处创建对象:

 fpsCounter = new FpsCounter();

在您的 draw() 函数中调用更新方法并更新 fps 显示:

function drawScene() {          
  fpsCounter.update();
  document.getElementById('fpsDisplay').innerHTML = fpsCounter.getCountPerSecond();
  document.getElementById('fpsMinuteDisplay').innerHTML = fpsCounter.getCountPerMinute();
  // Code   
}

注意:为了简单起见,我只将 fps-display 更新放在绘图函数中。对于 60fps,它每秒设置 60 次,即使每秒一次就足够了。

FpsCounter 代码:

function FpsCounter(){
    this.count = 0;
    this.fps = 0;
    this.prevSecond;  
    this.minuteBuffer = new OverrideRingBuffer(60);
}

FpsCounter.prototype.update = function(){
    if (!this.prevSecond) {     
        this.prevSecond = new Date().getTime();
            this.count = 1;
    }
    else {
        var currentTime = new Date().getTime();
        var difference = currentTime - this.prevSecond;
        if (difference > 1000) {      
            this.prevSecond = currentTime;
            this.fps = this.count; 
            this.minuteBuffer.push(this.count);
            this.count = 0;
        }
        else{
            this.count++;
        }
    }    
};

FpsCounter.prototype.getCountPerMinute = function(){
    return this.minuteBuffer.getAverage();
};

FpsCounter.prototype.getCountPerSecond = function(){
    return this.fps;
};

OverrideBuffer 代码:

function OverrideRingBuffer(size){
    this.size = size;
    this.head = 0;
    this.buffer = new Array();
};

OverrideRingBuffer.prototype.push = function(value){      
    if(this.head >= this.size) this.head -= this.size;    
    this.buffer[this.head] = value;
    this.head++;
};

OverrideRingBuffer.prototype.getAverage = function(){
    if(this.buffer.length === 0) return 0;

    var sum = 0;    

    for(var i = 0; i < this.buffer.length; i++){
        sum += this.buffer[i];
    }    

    return (sum / this.buffer.length).toFixed(1);
};

【讨论】:

    【解决方案4】:

    由于其他答案均未解决问题的“in WebGL”部分,因此在正确测量 WebGL 中的 FPS 时,我将添加以下重要细节。

    window.console.time('custom-timer-id');    // start timer
    
    /* webgl draw call here */                 // e.g., gl.drawElements();
    
    gl.finish();                               // ensure the GPU is ready
    
    window.console.timeEnd('custom-timer-id'); // end timer
    

    为简单起见,我使用了控制台计时器。我试图指出始终使用WebGLRenderingContext.finish() 来确保测量正确的时间,因为对 GPU 的所有 WebGL 调用都是异步的!

    【讨论】:

    • 这行不通。 WebGL 中的gl.finish 并没有按照你的想法做。即使在 OpenGL 中,这也不会为您提供准确的结果
    【解决方案5】:

    使用旋转数组可以做得更好。 带 dom 元素:

    &lt;div id="fps"&gt;

    以下脚本可以解决问题:

    var fpsLastTick = new Date().getTime();
    var fpsTri = [15, 15, 15]; // aims for 60fps
    
    function animate() {
      // your rendering logic blahh blahh.....
    
    
      // update fps at last
      var now = new Date().getTime();
      var frameTime = (now - fpsLastTick);
      fpsTri.shift(); // drop one
      fpsTri.push(frameTime); // append one
      fpsLastTick = now;
      fps = Math.floor(3000 / (fpsTri[0] + fpsTri[1] + fpsTri[2])); // mean of 3
      var fpsElement = document.getElementById('fps')
      if (fpsElement) {
        fpsElement.innerHTML = fps;
      }
    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-30
      • 1970-01-01
      相关资源
      最近更新 更多