使用 about:tracing 标志分析您的 WebGL 游戏

Lilli Thompson
Lilli Thompson

如果你无法衡量它,你就无法改进它。

开尔文勋爵

要让您的 HTML5 游戏运行得更快,您必须首先查明性能瓶颈,但这可能很困难。评估每秒帧数 (FPS) 数据是一个开始,但要了解全貌,您必须掌握 Chrome 活动中的细微差别。

about:tracing 工具提供的洞察力可帮助您避免仓促地采取旨在提高性能的权宜之计,而这些权宜之计本质上是善意的猜测。您将节省大量时间和精力,更清楚地了解 Chrome 在每一帧中执行的操作,并使用此信息来优化您的游戏。

你好 about:tracing

Chrome 的 about:tracing 工具为您提供了一个窗口,让您了解 Chrome 在一段时间内的所有活动,其粒度非常细,您一开始可能会觉得难以承受。Chrome 中的许多功能都已开箱即用地进行了跟踪检测,因此即使不进行任何手动检测,您仍然可以使用 about:tracing 来跟踪您的性能。(请参阅后面关于手动检测 JS 的部分)

要查看跟踪视图,只需在 Chrome 的地址栏(多功能框)中键入“about:tracing”即可。

Chrome omnibox
在 Chrome 的地址栏中键入“about:tracing”

从跟踪工具中,您可以开始录制,运行游戏几秒钟,然后查看跟踪数据。这是一个数据可能看起来像什么样的示例

Simple tracing result
简单的跟踪结果

是的,这确实令人困惑。让我们谈谈如何阅读它。

每一行代表一个正在分析的进程,左右轴表示时间,每个彩色框都是一个检测函数调用。有许多不同类型资源的行。对于游戏分析最有趣的是 CrGpuMain,它显示了图形处理单元 (GPU) 正在做什么,以及 CrRendererMain。每个跟踪都包含跟踪期间每个打开的选项卡的 CrRendererMain 行(包括 about:tracing 选项卡本身)。

读取跟踪数据时,您的首要任务是确定哪个 CrRendererMain 行对应于您的游戏。

Simple tracing result highlighted
突出显示的简单跟踪结果

在此示例中,两个候选者是:2216 和 6516。不幸的是,目前还没有一种完善的方法来挑选您的应用程序,除非查找正在进行大量定期更新的行(或者如果您已使用跟踪点手动检测了您的代码,则查找包含您的跟踪数据的行)。在此示例中,从更新的频率来看,6516 似乎正在运行主循环。如果您在开始跟踪之前关闭所有其他选项卡,则更容易找到正确的 CrRendererMain。但是,可能仍然存在用于游戏以外进程的 CrRendererMain 行。

查找您的帧

一旦您在跟踪工具中找到游戏的正确行,下一步就是查找主循环。主循环看起来像跟踪数据中的重复模式。您可以使用 W、A、S、D 键导航跟踪数据:A 和 D 向左或向右移动(在时间上前后移动),W 和 S 放大和缩小数据。如果您的游戏以 60Hz 运行,您会期望您的主循环是一个每 16 毫秒重复一次的模式。

Looks like three execution frames
看起来像三个执行帧

一旦您找到了游戏的心跳,您就可以深入了解您的代码在每一帧中到底在做什么。使用 W、A、S、D 放大,直到您可以读取函数框中的文本。

Deep into an execution frame
深入执行帧

此框集合显示了一系列函数调用,每个调用都由一个彩色框表示。每个函数都由其上方的框调用,因此在本例中,您可以看到 MessageLoop::RunTask 调用了 RenderWidget::OnSwapBuffersComplete,后者又调用了 RenderWidget::DoDeferredUpdate,依此类推。读取此数据,您可以全面了解什么调用了什么以及每次执行花费了多长时间。

但这就是有点棘手的地方。about:tracing 公开的信息是来自 Chrome 源代码的原始函数调用。您可以根据名称对每个函数的作用进行有根据的猜测,但这些信息并不完全用户友好。了解帧的整体流程很有用,但您需要一些更易于理解的内容来实际弄清楚发生了什么。

添加跟踪标记

幸运的是,有一种友好的方式可以将手动检测添加到您的代码中以创建跟踪数据:console.timeconsole.timeEnd

console.time("update");
update();
console.timeEnd("update");
console.time("render");
update();
console.timeEnd("render");

上面的代码在跟踪视图名称中创建了带有指定标记的新框,因此如果您重新运行应用程序,您将看到“update”和“render”框,这些框显示了每个标记的开始和结束调用之间经过的时间。

Tags added manually
手动添加的标记

使用此功能,您可以创建人类可读的跟踪数据来跟踪代码中的热点。

GPU 还是 CPU?

对于硬件加速图形,您在分析期间可以问的最重要的问题之一是:此代码是受 GPU 限制还是受 CPU 限制?在每一帧中,您将在 GPU 上进行一些渲染工作,并在 CPU 上进行一些逻辑处理;为了了解是什么导致您的游戏变慢,您需要了解工作是如何在两个资源之间平衡的。

首先,在名为 CrGPUMain 的跟踪视图中找到该行,该行指示 GPU 在特定时间是否繁忙。

GPU and CPU traces

您可以看到游戏的每一帧都会在 CrRendererMain 以及 GPU 上引起 CPU 工作。上面的跟踪显示了一个非常简单的用例,其中 CPU 和 GPU 在每个 16 毫秒帧的大部分时间里都处于空闲状态。

当您的游戏运行缓慢并且您不确定哪个资源已达到最大值时,跟踪视图确实会变得很有帮助。查看 GPU 和 CPU 行之间的关系是调试的关键。采用与之前相同的示例,但在更新循环中添加一些额外的工作。

console.time("update");
doExtraWork();
update(Math.min(50, now - time));
console.timeEnd("update");

console.time("render");
render();
console.timeEnd("render");

现在您将看到一个看起来像这样的跟踪

GPU and CPU traces

此跟踪告诉我们什么?我们可以看到,图中所示的帧从大约 2270 毫秒到 2320 毫秒,这意味着每一帧大约需要 50 毫秒(帧速率为 20Hz)。您可以看到代表渲染函数的彩色框条紧挨着更新框,但帧完全由更新本身主导。

与 CPU 上发生的情况相反,您可以看到 GPU 在每个帧的大部分时间里仍然处于空闲状态。为了优化此代码,您可以查找可以在着色器代码中完成的操作,并将它们移动到 GPU 以充分利用资源。

如果着色器代码本身速度很慢并且 GPU 工作过度怎么办?如果我们删除 CPU 上不必要的工作,而是在片段着色器代码中添加一些工作会怎么样。这是一个不必要地昂贵的片段着色器

#ifdef GL_ES
precision highp float;
#endif
void main(void) {
  for(int i=0; i<9999; i++) {
    gl_FragColor = vec4(1.0, 0, 0, 1.0);
  }
}

使用该着色器的代码的跟踪是什么样的?

GPU and CPU traces when using slow GPU code
使用慢速 GPU 代码时的 GPU 和 CPU 跟踪

再次注意帧的持续时间。这里的重复模式从大约 2750 毫秒到 2950 毫秒,持续时间为 200 毫秒(帧速率约为 5Hz)。CrRendererMain 行几乎完全为空,这意味着 CPU 大部分时间都处于空闲状态,而 GPU 则过载。这清楚地表明您的着色器太重了。

如果您看不到到底是什么导致了低帧率,您可能会观察到 5 Hz 的更新,并试图进入游戏代码并开始尝试优化或删除游戏逻辑。在这种情况下,这样做绝对没有任何好处,因为游戏循环中的逻辑并不是占用时间的原因。事实上,此跟踪表明,每帧执行更多 CPU 工作基本上是“免费的”,因为 CPU 处于空闲状态,因此给它更多工作不会影响帧的持续时间。

真实示例

现在让我们看看来自真实游戏的跟踪数据是什么样的。使用开放 Web 技术构建的游戏的酷炫之处在于,您可以看到您最喜欢的产品中正在发生的事情。如果您想测试分析工具,您可以从 Chrome Web Store 中选择您最喜欢的 WebGL 游戏,并使用 about:tracing 进行分析。这是从出色的 WebGL 游戏 Skid Racer 中提取的示例跟踪。

Tracing a real game
跟踪真实游戏

看起来每一帧大约需要 20 毫秒,这意味着帧速率约为 50 FPS。您可以看到工作在 CPU 和 GPU 之间是平衡的,但 GPU 是需求最大的资源。如果您想了解分析 WebGL 游戏的真实示例是什么样的,请尝试玩一些使用 WebGL 构建的 Chrome Web Store 游戏,包括

结论

如果您希望您的游戏以 60Hz 运行,那么对于每一帧,您的所有操作都必须在 16 毫秒的 CPU 时间和 16 毫秒的 GPU 时间内完成。您有两个可以并行使用的资源,您可以在它们之间转移工作以最大化性能。Chrome 的 about:tracing 视图是深入了解您的代码实际在做什么的宝贵工具,它将通过解决正确的问题来帮助您最大化您的开发时间。

下一步是什么?

除了 GPU 之外,您还可以跟踪 Chrome 运行时的其他部分。Chrome Canary(Chrome 的早期版本)已检测到可以跟踪 IO、IndexedDB 和其他几项活动。您应该阅读这篇 Chromium 文章,以更深入地了解跟踪事件的当前状态。

如果您是 Web 游戏开发者,请务必观看下面的视频。这是 Google 游戏开发者布道团队在 GDC 2012 上关于 Chrome 游戏性能优化的演示