为什么GPU渲染时间不一致?

时间:2018-11-05 19:34:49

标签: javascript three.js webgl

我正在使用三个构建WebGL应用程序,并注意到GPU上的时间有些奇怪。我目前没有提供repro代码,但我想我会问这个问题,以防它是已知的浏览器怪癖或常见且可修复的东西。

场景设置

  • 具有约2,000,000个多边形,136个网格和568个Object3D实例的场景。
  • 与FXAA和Unreal Bloom通行证一起使用三个Composer。
  • 使用三个OrbitControls。
  • 仅当已知发生某些更改时才渲染场景。例如,当用户拖动场景以使用控件移动摄像机或场景中的某些东西移动时,将计划绘制。场景通常是静态的,因此在这些情况下我们尽量不要不必要地渲染。

问题

当场景是静态的(暂时未绘制),然后用户通过拖动更改摄像机位置时,会发生此问题。一旦用户开始拖动,帧速率将非常不稳定-可能为10-20 fps或更低-连续几帧,然后平滑回到接近60的水平。这种情况在将场景放置几秒钟然后再次拖动时始终会发生。如果在初始断断续续之后始终拖动鼠标,则帧速率保持平稳。这些帧没有任何变化。

这种结结不会发生,并且如果使用requestAnimationFrame渲染每一帧,场景仍然会保持活泼。

这是只有在某些情况发生变化时才渲染场景时带有断断续续的性能分析器。您会发现在帧间停顿之前,在GPU上花费了更多时间,然后再次平滑:

enter image description here

以60 fps渲染场景时的事件探查器:

enter image description here

有什么想法吗?为什么在拖曳下突然发生了太多的GPU工作?抽奖会被其他渲染过程阻止吗?在几秒钟不渲染后,为什么会如此持续地发生呢?我已经使用最新版本的Chrome进行了配置文件,但Firefox中也存在断断续续的情况。

谢谢!

1 个答案:

答案 0 :(得分:0)

没有实时样本,就没有简单的方法来了解BUT。

1 Three.js可以对对象进行视锥剔除。

这意味着如果某些对象不在视图之外,则不会绘制它们。因此,将摄像头摆放得比所有仅可见的物体慢一些

2基本裁剪

与上述相同,但在GPU级别上除外。 GPU会裁剪图元(它不会在视图外部绘制或计算像素),与上面的类似,如果您要绘制的很多东西恰好在视图外部,则它的运行速度将比所有内容都在视图内部的运行速度快视图。

3 Depth(Z)缓冲区拒绝

再次类似于上面,如果您的对象是不透明的,则通过深度测试,如果某个像素位于现有像素的后面,则GPU将在可能的情况下跳过调用像素着色器的操作。这意味着,如果您绘制568个物体,并且绘制的第一个物体是最接近相机的物体,并且遮盖了其背后的许多物体,则比其先绘制所有这些物体之后的运行速度要快。 Three.js可以选择在绘制之前进行排序。通常需要打开排序以提高透明度,因为需要将透明对象向后绘制。对于不透明的对象,如果将任何前面的对象遮挡住更靠后的对象,则从前向后进行绘制会更快。

4绘制过多帧?

另一个问题是您如何排队抽奖?理想情况下,您只需要排队一次抽签,直到抽签结束,再也不要排队。

所以

// bad
someElement.addEventListener('mousemove', render);

上面的代码将尝试为每一次鼠标移动呈现渲染,即使速度> 60 fps

// bad
someElement.addEventListener('mousemove', () => {
  requestAnimationFrame(render);
});

上面的代码可能会使很多的requestAnimationFrames排队,所有的请求动画都将在下一帧执行,每帧多次绘制场景

// good?
let frameQueued = false;

function requestFrame() {
  if (!frameQueued) {
    frameQueued = true;
    requestAnimationFrame(render);
  }
}

function render(time) {
  frameQueued = false;
  ...
}      


someElement.addEventListener('mousemove', () => {
  requestFrame();
});

或者类似的方式,这样您最多只能在渲染上排队,而在渲染完成之前不要再排队。上面的代码只是结构代码的一种示例,这样您就不会画出过多的帧。