WebGL性能
WebGL的声誉不佳:开发者认为WebGL很慢,无法渲染复杂的3D图形。许多示例表明情况并非如此,但开发者的经验却一次又一次地强化了这种观点。
运行WebGL代码的硬件并未对浏览器进行降速处理。那么,为什么开发者会认为在网页上渲染快速3D图形是不可能的呢?
本文将指导您了解WebGL性能,以及如何在网页上实现快速的3D渲染。
CPU vs GPU
在3D应用程序中,一些代码在CPU上运行,而一些代码在加速硬件——GPU上运行。
WebGL的设计旨在利用GPU加速网页上的图形应用程序。因此,WebGL可以理解为将任务发送到图形硬件并获取结果的函数。
为GPU创建任务的代码在CPU上运行,对于WebGL,我们通过JavaScript进行控制。
浏览器开销
由于浏览器的目标是保护用户免受潜在的恶意网站的侵害,它会通过JavaScript检查网站进行的每个WebGL调用的安全性。它还需要隔离运行网站代码的进程,任何对这个进程的调用都需要转换成可发送的格式,这称为“编组”。
编组和检查网站的WebGL调用都会增加与同样调用在本地环境中的表现相比的性能开销。
避免WebGL调用
为实现WebGL性能,我们应避免使用高开销的调用。你可以通过分析你的WebGL应用程序轻松找出哪些调用尤其昂贵。
以下是一些反直觉地昂贵的调用:
避免这些调用的一个绝佳方法是进行WebGL状态跟踪,并在WebGL应用程序的生产版本中减少错误检查。
避免绘制调用
WebGL性能的秘密在于尽可能减少绘制调用。绘制调用是指使用以下WebGL函数的任何函数:
- drawArrays()
- drawElements()
- drawArraysInstanced()
- drawElementsInstanced()
- drawBuffers()
- drawRangeElements()
有很多方法可以做到这一点。比如,使用一个函数调用来绘制多个对象,而不是多次调用同一个函数来绘制相同的对象。你可以使用实例化来渲染大量的相同网格,或使用WEBGL_multi_draw 使用相同的着色器渲染许多不同的对象。
3D引擎通常有一个称为“批处理”的功能,它会将许多调用合并到一个WebGL调用中。Wonderland Engine将此推向极致,在自动渲染时,将场景中的数万个动态对象减少到不到十个绘制调用。
移动设备上的WebGL性能尤其受绘制调用的影响。
Safari上的WebGL
在Safari(iOS和MacOS)上,一些WebGL调用有着意想不到的大量开销,例如:
在为Safari优化时,特别注意Uniform Buffers的使用。
JavaScript
用于与浏览器API(如WebGL)交互的语言是JavaScript。JavaScript的主要性能问题是垃圾回收,它会自动查找并删除不再需要的内存。
垃圾回收过程可能会使WebGL应用程序的性能变得不可预测和不可靠,因为你无法控制它发生的时间。因此,它通常会在可能导致渲染卡顿的时候发生,导致性能不佳的表现,即使代码在其他方面是经过优化的。
为了产生稳定的帧率,需严格地编写无垃圾的JavaScript。
iOS Safari和内存
在Safari上能运行良好的WebGL应用很少。Safari上的WebGL性能增加了一些额外的挑战。我们撰写了一整篇博客文章,介绍如何为Safari优化。
内存限制
在iOS上,你将面临另一个挑战:浏览器标签页的内存非常有限,尤其是在较旧的iPhone硬件上。如果超出内存限制,标签页可能会重新加载或冻结。由于iPhone使用统一内存,CPU和GPU共享RAM,因此纹理和缓冲数据,以及JavaScript或WebAssembly内存都会算入限制。