我们如何分析 WebXR/WebGL 应用
本文介绍了分析 WebXR 和 WebGL 应用的方法(与底层框架无关)。分析是优化应用的第一步。不进行分析的优化等于浪费精力,正如我们在瓶颈部分所描述的。
请注意,本文并未涉及如何解决发现的性能问题。市面上有很多博客文章和演讲专门针对特定的瓶颈,一旦你知道问题所在,你就能找到解决方案。
我们即将分析的指标包括 CPU 时间、GPU 时间、JavaScript 堆内存以及应用加载时间。我们的目标是提供一套最全面、免费的分析信息。并且不:多边形计数”不是我们关心的指标!
内容
瓶颈
如果不找出应用的瓶颈,你的优化努力将不具成效。
示例
假设你在渲染一个包含许多对象的大型场景。你的应用勉强达到 60 fps。即使你优化了每个对象的顶点数,帧率可能仍然维持在 60 fps。
在这种情况下,CPU 负责将渲染每个对象的命令(“绘制调用”)发送给 GPU。很可能 CPU 的任务过于繁重,导致 GPU 在空闲,等待 CPU 发送更多工作任务。
不过,我们并不知道……直到我们进行测量。这就是分析存在的意义。
瓶颈类型
应用的任何部分都可能导致瓶颈。以下是一些常见瓶颈类型的例子,按发生频率排序:
CPU 绘制调用:过多的驱动调用导致大量开销。
垃圾:应用正常运行,但产生的垃圾会导致规律或不规律的卡顿。
CPU 逻辑:应用无法快速为 GPU 提供工作。通常由于繁重的物理模拟或光线投射引起。
GPU 片段着色:每个片段的性能成本过高。
GPU 顶点处理:每个顶点的处理需求过大。
GPU 解析:由移动 GPU 上的后期处理导致。
GPU 顶点读取:顶点内存读取速度不够快。
每一种瓶颈可能还有其子瓶颈。
指标
我们将要分析的指标包括:
CPU 时间
任何 XR 应用的工作都分为 CPU 和 GPU。CPU 负责为 GPU 准备图形工作并执行应用逻辑。
任何 JavaScript 代码都会在 CPU 上运行,以准备绘制调用、执行资源加载、模拟物理、渲染音频和计算场景图转换。
GPU 时间
GPU 承担图形处理的重任。虽然 CPU 会发送像“用着色器 Y 和纹理 Z 以及材质参数 W 绘制网格 X”这样的绘制调用,但 GPU 会实际进行光栅化和每个顶点的转换以生成屏幕上的像素。
GPU 还负责将最终图像传输到屏幕,并等待“V-Sync”来同步屏幕的刷新率与应用程序的更新速度。
帧率与 V-Sync
我们不使用帧率(每秒帧数 = fps)来分析我们的应用。
这个指标仅用于解释理解性能与 V-Sync 交互的复杂性。它太粗略无法做出有效判断。
将 V-Sync 想象为_固定的截止时间_:每秒 60、72 或 90 次(帧率)。我们把“达到 V-Sync”称为及时渲染并提交帧以赶上截止时间。
当你错过截止日期时,所有工作都会被丢弃,你需要努力赶上下一个截止日期。 在可变帧率环境中,这可能意味着驱动可能会将帧速率降至一半。
如果应用界面仅比目标速度稍慢,你可能看到 30 fps 而不是 59 fps,从而传达一个完全不同的信息。可能应用正以 3ms 的 CPU 时间和 3ms 的 GPU 时间表现不错,但由于工作开始较晚导致错过 V-Sync,从而帧率减半。 此时,没有顶点计数、绘制调用或其他经典优化能帮助你。
延迟
延迟,即从输入(如头部运动)到形成最终帧的时间差,对于 VR 来说尤为重要。WebXR 实现通常会帮我们处理这个问题,如果我们没有用完帧预算,可能会稍微推迟一些帧回调,以减少延迟。在 WebXR 中对此的控制有限,因此本文中不作深入探讨。
垃圾回收
JavaScript 带有内存管理功能,以至于你能够随意进行分配。因此很容易掉以轻心。
由于不需要管理内存,你便失去了在何时管理内存的控制权。这个过程称为“垃圾回收”,会随意地在应用程序生命周期中发生——例如,当你即将达成 V-Sync 并提交帧时。
垃圾回收可能会消耗 0.1 - 10 毫秒。考虑到通常操作 VR 的帧预算是 11 毫秒,你_绝对_不希望这种情况随机发生。
那么我们该如何避免它?唯一的方法就是避免_任何_需要清理的垃圾产生。你生成得越少,垃圾回收间歇就会越小越少。
应用加载时间
与网站加载类似,用户对使用应用程序的兴趣会随等待时间的增加而减退。由于 WebXR 应用程序相对较大,可能会与网站有所不同,但这并不是必要的。
通过提前加载尚不需要的资源,我们可以减少感知的加载时间。
通过优化资源并确保服务器设置最佳,我们可以减少总加载时间。
通过使用解析耗费较少的格式,我们可以减少 CPU 在下载资源后所需的工作。
工具
在本文中,我们将介绍以下工具:
- Chrome Profiler,分析[CPU, GPU, 垃圾]
- Chrome Networking tab,分析[加载时间]
- OVR Profiler Tool,分析[CPU, GPU]
- Spector.js 浏览器插件,分析[CPU, GPU]
- WebGL Disjoint Timer Query,分析[GPU]
- Wonderland Editor Profiler(Wonderland Engine 专用)。分析[CPU, GPU]
Chrome Profiler
Chrome 自带的分析器可以让你分析 JavaScript 的 CPU 时间、垃圾回收以及粗略的 GPU 帧时间。
你可以通过访问任何网站并按下 Ctrl + Shift + C
(MacOS 上为 Command + Shift + C
)找到性能标签。找到“Performance”。
要记录分析会话,点击左上角的记录按钮(Ctrl/Command + E
)。
通常 3-5 秒就足够了,因为我们关注的通常是个别帧。
在Android 设备上的远程调试时同样有效,比如 Meta Quest 或智能手机。
Safari 在 Mac、iOS 设备和 Apple Vision PRO 上具备类似的分析功能(例如,通过 Apple Vision PRO 模拟器运行的 Safari)。
Chrome 内存分析器
在垃圾回收部分,我们谈到原本较为流畅的应用程序由于垃圾收集而产生卡顿。
为追踪这个瓶颈,浏览器提供了一种方法来采样 JavaScript 堆。在前面提到的Chrome Profiler中,你可以这样在 Chrome 中启用它:

示例
以下是在 Meta Quest 2 上基于 Three.js 的 Elysian 的分析:
你可以看到“JS Heap”在 14.1 MB 到 23.3 MB 之间波动。
每当内存快速下降,就发生了垃圾回收。在这个例子中,GC 所占用的时间很长,以至于推迟了下一帧的开始,导致帧丢失:

结果,我们只能希望丢失的帧被前一帧成功预测重建,否则会发生严重的卡顿。 由于动画无法通过预测重建,因此无论如何都会有一些卡顿。
网络选项卡
任何浏览器的网络选项卡都是分析应用程序加载时间的得力工具。
你可以通过访问任何网站并按下 Ctrl + Shift + C
(Mac 上为 Command + Shift + C
)找到网络选项卡。
在打开选项卡时重新加载页面可记录网络活动。
下载阻塞
如果某个资源在应用程序启动时不需要立即使用,应该延迟加载,因为浏览器将在 有限的并行请求内运行(例如,Chrome 是 6 个)。
Meta Quest 性能 HUD
在 Meta Quest 上分析 VR 渲染时,可利用性能 HUD进行操作。
在 Meta Quest Developer Hub 中可以轻松安装。
这对调试视图相关的性能问题特别有用,因为你能在使用头显时得到持续的性能反馈。
示例
在以下示例中,Ayushman Johri 分享了他的基于 Wonderland Engine 的“Vintage Study Room”在 Meta Quest 性能 HUD 下的优越性能表现:
这是用 @MetaQuestVR 的 FPS 图表进行的性能测试!
— Ayushman Johri ✨ (@AyushmanJohri) August 19, 2023
我会提到,与不记系统录相比,我观察到了更多的陈旧帧~
出于艺术选择,我决定在一些额外细致的资产使用未压缩的纹理 ✨
虽然不完美,但很接近! pic.twitter.com/MLzWVLHItn
OVR GPU Profiler
如果你遇到顶点或片段着色瓶颈,则需要更清晰地理解你的着色器中哪里耗时。
ovrgpuprofiler 是一个极为犀利的工具。对 GPU 内存和架构有些了解的话,它会给你很大的洞察。
通过 adb
将其安装到你的 Meta Quest 设备上,并通过 adb shell
运行:
(来源:developer.oculus.com)
示例输出如下所示:
(修改自:developer.oculus.com)
你可能听说过,在着色器程序中读写内存通常是最昂贵的操作。但其昂贵程度取决于内存操作是否对缓存友好。
L1(“一级”)高速缓存内存速度非常快。要确保利用它,请遵循局部性原则:访问前后彼此接近的内存位置。
Spector.js
Spector.js 是适用于 Chrome 和 Firefox 的浏览器扩展,可捕获 WebGL 帧跟踪。它能显示完整的命令列表,并总结统计数据,如顶点数和绘制调用。
从Chrome 扩展商店或Firefox 插件库中安装。你还可以通过 HTML 标签将该工具嵌入不支持插件的浏览器中。

首先通过顶部的插件按钮启用工具,然后点击红色的记录按钮录制帧。

你可以看到,在这里,Wonderland Engine 绘制了许多对象,总共使用了 11 次绘制调用。对于其他框架,这个数字通常会是它的 10-100 倍。
Disjoint Timer Query
EXT_disjoint_timer_query 扩展是一个 WebGL 扩展,可用于测量一组 WebGL 命令的 GPU 时间。在 Chrome 中支持较好[截至 2023 年 8 月],并需启用“WebGL Debug Extensions”Chrome 标志。
由于所有命令在 GPU 上异步运行,因此时间测量也需要异步调度。
Wonderland Editor Profiler
Wonderland Editor 自带分析工具,有助于理解你的 WebXR 应用程序在性能方面可能遭到的挑战。
结语
每一种框架都有其独特的性能特征。大多数会因绘制调用而在其他方面前瓶颈,之后是片段瓶颈——不仅限于90 fps,无需去低多边化!
我们从头规划 Wonderland Engine 避免大多数上述瓶颈——免费高达每年120k 美元的收入。 如需企业许可证和支持,请通过[email protected]联系我们。
立即试用 Wonderland Engine 并开始节省时间优化。
