通过 setInterval 限制 WebGL 帧率会严重降低性能

3

我正在制作一个WebGL游戏,我已经做了相当不错的优化,但是有一个问题,我的帧率限制器影响性能。我知道你在想什么:“当然会影响啊,它是一个FPS限制器”。但问题是它没有按预期行事。这里是代码:

renderTimer = null;
function animate() {
  clearTimeout(renderTimer);
  renderTimer = setTimeout(function () {
    _frame = requestAnimationFrame(animate);
  }, 33);
  render();
}

function render(){
  // operations for mesh positioning/animation
  handleObjects();
  renderer.render(scene, camera);
}

在我的台式电脑上,一切都很正常,游戏玩起来很流畅,保持在29-30帧每秒的速度。
但是在我的笔记本电脑上,帧率会降至22-24,并且游戏体验不顺畅。如果我将间隔延迟时间更改为16ms,则游戏体验相对流畅,保持在约35fps左右。如果完全删除间隔,则游戏体验完全流畅,保持在约45fps左右。
我并不完全理解这种行为。如果帧率设定为30fps,那么为什么我的笔记本电脑表现会低于25fps?我期望它没有间隔时间时也应该是25fps,但事实上它更快了。有点好奇。
虽然我很乐意去掉间隔时间,但我确实希望我的帧率限制为30fps。因为一些玩家比其他玩家拥有更高的帧率,这会使他们处于优势地位。
你有什么想法吗?
1个回答

3
有几个要考虑的问题:
  • 在JavaScript中,setTimeout不能保证非常准确。
  • 浏览器可以在任何时候执行自己的操作,例如垃圾回收,从而延迟处理时间。
  • 渲染本身(webgl / three.js + 您自己的游戏逻辑)需要时间。即使您在主渲染调用之前创建超时,它仍会引入33毫秒的空闲时间。

好吧,现在我想起来了,我对最后一点不太确定。无论如何,我观察到了类似的问题,我设法制定了一个相当不错的解决方案,它可以保持帧速率平稳,并对目标帧速率精确到1-2 FPS(如果计算机可以处理此帧速率)。尽管这是一个hack。

首先,您可以查看Three.js中的requestAnimationFrame实现(适用于没有内置该功能的浏览器):

requestAnimationFrame = function ( callback ) {
  var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
  var id = self.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
  lastTime = currTime + timeToCall;
  return id;
};

你可以看到它会根据上一次调用进行setTimeout的调整(目标大约为60FPS)。因此这是你可以尝试的一种解决方案。
我所做的(似乎很好用的hack)是将timeout值作为一个变量,初始值为原始的33或者所需帧率。然后在每一帧中,我记录Date.now()并将其与上一帧时间进行比较。如果我们错过了预算的帧时间,我们将timeout值减1。如果我们比所需的要快,我们将timeout值加1。因此,代码不断、平滑地调整timeout以匹配所需的帧速率。通过只略微增加/减少,我们避免了无法预测的垃圾回收等问题完全扰乱计算并搞乱事情。而且它就是这样(商标)
我不会发布代码,因为我的渲染循环中还有很多其他内容(只有在发生更改时才呈现新帧等),将其隔离成一个相关的代码示例会很繁琐。

有趣。所以在你的例子中,你是在覆盖现有的requestAnimationFrame函数吗?递增超时解决方案很有趣,下班后我会试一试。 - Hobbes
requestAnimationFrame是Three.js中包含的shim的复制粘贴,以解决浏览器不支持它的问题。这似乎是自动调整超时值的一个很好的例子。我的实现稍微扩展了这个想法(我没有覆盖requestAnimationFrame,所有的操作都在animate函数和相关函数中完成)。 - yaku
啊,好的,明白了,迫不及待地想试一下 =] - Hobbes
变量目标率方法非常好用,谢谢! - Hobbes
@yaku:我尝试使用你的方法,如果我想将FPS限制在30以下,它可以正常工作。但是,如果我想将FPS限制在45以下,似乎就不起作用了。实际上,只需在我的动画中添加一个setTimeout(如下所示),FPS就会降至30。你能否分享一下你的发现?`function animate(ts) { setTimeout(function () { requestAnimationFrame(animate); }, 1000/60); //进行渲染 }` - Ravi Gidwani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接