Safari 中的 WebGPU 和 WSL
WebGPU 是由 Apple 和 W3C 中其他方正在开发的新 API,它支持 Web 上的高性能 3D 图形和数据并行计算。与现有的 WebGL API 相比,此 API 在性能和易用性方面都有显著改进。从 Safari 技术预览版 91 开始,WebGPU API 和 WSL(我们对 WebGPU 着色语言的提案)的 Beta 支持已可用。
几个月前,我们讨论了一个名为 Web 高级着色语言 (Web High-Level Shading Language) 的新着色语言提案,并开始将其作为概念验证进行实现。从那时起,我们改变了对这种新语言的方法,我将在本文后面讨论。在 Babylon.js 朋友的帮助下,我们能够调整一个演示以使其与 WebGPU 和 Web 着色语言 (WSL) 协同工作。
您可以使用 Safari 技术预览版 91 或更高版本,并在启用 WebGPU 实验性功能的情况下查看演示的实际效果。1 该演示利用了 WebGPU 的许多最佳功能。让我们深入探讨 WebGPU 为什么能如此出色地工作,以及为什么我们认为 WSL 是 WebGPU 的绝佳选择。
WebGPU JavaScript API
就像 WebGL 一样,WebGPU API 是通过 JavaScript 访问的,因为大多数 Web 开发人员已经熟悉在 JavaScript 中工作。我们预计未来会创建一个通过 WebAssembly 访问的 WebGPU API。
流水线状态对象和绑定组
大多数 3D 应用程序渲染不止一个对象。在 WebGL 中,每个对象在渲染之前都需要一系列状态更改调用。例如,在 WebGL 中渲染单个对象可能看起来像
gl.UseProgram(program1);
gl.frontFace(gl.CW);
gl.cullFace(gl.FRONT);
gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_MIN);
gl.blendFuncSeparate(gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA, gl.ZERO);
gl.colorMask(true, false, true, true);
gl.depthMask(true);
gl.stencilMask(1);
gl.depthFunc(gl.GREATER);
gl.drawArrays(gl.TRIANGLES, 0, count);
另一方面,在 WebGPU 中渲染单个对象可能看起来像
encoder.setPipeline(renderPipeline);
encoder.draw(count, 1, 0, 0);
WebGL 示例中所有的状态片段都封装在 WebGPU 中的单个对象中,名为“流水线状态对象”。尽管验证状态开销很大,但在 WebGPU 中,它是在创建流水线时一次性完成的,位于核心渲染循环之外。因此,我们可以避免在绘制调用内部执行昂贵的状态分析。此外,设置整个流水线状态是一个单一的函数调用,减少了 JavaScript 和 WebKit 的 C++ 浏览器引擎之间的“通信”量。
资源也类似。大多数渲染算法都需要一组资源才能绘制特定材质。在 WebGL 中,每个资源都会逐一绑定,导致代码看起来像
gl.bindBufferRange(gl.UNIFORM_BUFFER, 0, materialBuffer1, 0, size1);
gl.bindBufferRange(gl.UNIFORM_BUFFER, 1, materialBuffer2, 0, size2);
gl.activeTexture(0);
gl.bindTexture(gl.TEXTURE_2D, materialTexture1);
gl.activeTexture(1);
gl.bindTexture(gl.TEXTURE_2D, materialTexture2);
然而,在 WebGPU 中,资源被批量打包成“绑定组”。使用 WebGPU 时,上述代码的行为简单表示为
encoder.setBindGroup(0, bindGroup);
在这两个示例中,多个对象被收集在一起并烘焙成硬件相关的格式,此时浏览器执行验证。能够将对象验证与对象使用分离意味着应用程序开发者可以更好地控制昂贵操作在其应用程序生命周期中何时发生。
运行时性能
我们期望 WebGPU 比 WebGL 表现更快,并处理更大的工作负载。为了衡量性能,我们采用了来自我们的 2D 图形基准测试 MotionMark 的测试工具,并编写了两个版本的性能测试,它们具有简单而真实的工作负载,一个使用 WebGL,另一个使用 WebGPU。
该测试衡量在保持每秒 60 帧的情况下可以绘制多少具有不同属性的三角形。每个三角形都通过不同的绘制调用和绑定组进行渲染;这模仿了许多游戏在不同的绘制调用中使用不同的资源渲染对象(如角色、爆炸、子弹或环境对象)的方式。由于 WebGPU 中的大部分验证逻辑是在创建绑定组期间而不是在绘制调用内部执行的,因此这两个调用都比 WebGL 中的等效调用执行得快得多。

Web 着色语言
WebGPU 社区小组正在积极讨论该规范应该支持哪种或哪些着色语言。去年,我们提出了一种新的 WebGPU 着色语言。我们很高兴地宣布,此语言的 Beta 版本已在 Safari 技术预览版 91 中提供。
由于社区反馈,我们设计该语言的方法已经演变。此前,我们设计该语言是为了与 HLSL 源代码兼容,但我们意识到这种兼容性并非社区所要求的。许多人希望着色语言尽可能简单和低级。这将使其成为开发团队使用的任何语言更易于编译的目标。
如今许多 Web 开发人员在 WebGL 中使用 GLSL,因此接受另一种高级语言(如 HLSL)的潜在浏览器将无法很好地满足他们的需求。此外,像 HLSL 这样的高级语言无法在 WebGPU 设计运行的每个平台和图形 API 上忠实地执行。
因此,我们决定让该语言更简单、更低级且编译速度更快,并将其重命名为 Web 着色语言 (Web Shading Language) 以符合这一追求。2
名称的改变反映了我们方法的转变,但 WSL 的主要原则没有改变。它仍然是一种高性能语言。它仍然是基于文本的,与现有 Web 技术和文化良好融合。WSL 的语义仍然内置了安全性和可移植性。它仍然是一个出色的编译目标。正如所有 Web 技术所期望的那样,该语言通过完善的规范得到了良好定义。而且由于 WebGPU 社区小组拥有该语言,它与 WebGPU API 协同成熟。
在即将发布的文章中,我们将更详细地讨论此语言更新的技术细节。在这里,让我们进一步讨论 WSL 的性能特征。
网络传输大小
上面的 Babylon.js 演示展示了 WSL 真正出彩的领域。演示中的着色器使用复杂的基于物理的算法,以尽可能高的真实度进行绘制。Babylon.js 通过在运行时将着色器源代码片段拼接在一起来创建这些复杂的着色算法。通过这种方式,Babylon 的着色器在运行时会根据应用程序包含的特定内容类型进行调整。由于 WSL 是一种像 GLSL 一样的文本语言,因此这种工作流程可以通过简单的字符串拼接来实现。
通过字符串拼接构建着色器并直接编译,与那些无法轻松进行这种操作的语言(如基于字节码的语言)相比,是一个显著的改进。对于这些其他语言,在运行时生成着色器需要执行一个额外的编译器(用 JavaScript 或 WebAssembly 编写),将生成的源文本编译成字节码形式。
这个额外的步骤会以两种方式降低性能。首先,额外编译器的执行需要时间。其次,编译器源代码必须与网页的其余资源一起提供,这会增加页面大小并延长网页加载时间。另一方面,由于 WSL 的格式是文本,没有额外的编译步骤;您可以直接编写并运行它!
作为比较,我们来看上面显示的 Babylon.js 演示。Babylon.js 通过在运行时拼接 GLSL 着色器片段来渲染场景。使用 WSL 时,Babylon.js 将简单地提供 WSL 片段,就像他们今天提供 GLSL 片段一样。要使用 SPIR-V 做同样的事情,Babylon.js 将提供其着色器片段以及一个将这些文本片段编译为 SPIR-V 的编译器。Chrome 最近宣布他们使用相同的演示支持带有 SPIR-V 的 WebGPU。以下是 WSL 格式着色器和 GLSL 格式着色器与 SPIR-V 编译器的 gzip 压缩网络传输大小的比较

顶部的条形图显示了 Babylon.js 直接使用 WSL 时的网络传输大小。中间的条形图显示了 GLSL 源的网络传输大小,加上 Babylon.js 用于将其片段编译成 SPIR-V 的编译器。底部的条形图显示了如果 Babylon.js 仅提供其 SPIR-V 编译结果时的网络传输大小。由于 Babylon.js 着色器的动态特性,它们在运行时组装着色器并动态编译它们,因此需要 SPIR-V 编译器。然而,其他网站的着色器可能更静态,在这种情况下,它们不需要编译器,可以直接提供 SPIR-V,就像上面图表中的底部条形图一样。
编译性能
我们从开发者那里听说,着色器编译性能极其重要。社区小组解决这个问题的一种方式是设计 WebGPU 以支持异步编译,这避免了阻塞后续渲染命令。然而,异步编译并非万能药。如果下一个渲染命令使用当前正在编译的着色器,则该渲染命令必须等待编译完成。因此,我们投入了大量精力来加快 WSL 的编译时间。
WebGPU 社区创建了一个计算着色器来演示“boids”的群集特性,我们将该示例变成了编译器性能基准测试。在这里,我们比较了 WSL 的编译时间与作为 WebAssembly 库运行的 GLSL 到 SPIR-V 编译器的运行时表现

如您所见,我们正在持续优化编译性能,并且在 STP 91 发布期间和发布之后,性能都在不断提升。
试用 WebGPU!
我们很高兴能在最新版本的 Safari 技术预览版中实现了 WebGPU 和 WSL 的 Beta 版本。请试用并告诉我们它对您的效果如何!我们渴望获得反馈,以便我们能够改进 WebGPU 和 WSL 语言,使其更好地满足每个人的需求。
请务必查看我们的WebGPU 示例库。我们将不断更新此页面,提供最新演示。非常感谢 Babylon.js 的 Sebastien Vandenberghe 和 David Catuhe 在 Safari 中对 Babylon.js 的帮助。
欲了解更多信息,您可以联系我:mmaxfield@apple.com 或 @Litherum,或者您可以联系我们的宣传大使 Jonathan Davis。
1 要启用 WebGPU Beta 支持,请在“开发”菜单中,选择“实验性功能”>“WebGPU”。
2 不过,我们仍然将其发音为“whistle”。