WebGPU 是一种新兴的 API,它提供对 Web 上硬件的图形和计算功能的访问。它是在 W3C 的 WebGPU 工作组 中由所有主要浏览器供应商以及英特尔和其他一些公司从头开始设计的,遵循以下原则
我们很高兴将 WebGPU 支持带到 Firefox,因为它将允许更丰富、更复杂的图形应用程序在 Web 上可移植地运行。由于使用了现代概念和一流的 WASM (WebAssembly) 支持,它也将使 Web 平台更容易为那些如今主要针对现代原生平台的团队所用。
API 概念
WebGPU 旨在建立在现代图形 API 之上:Vulkan、D3D12 和 Metal。向用户公开的构造反映了这些低级 API 的基本原语。让我们一起浏览 WebGPU 的主要构造,并通过 WebGL 的背景来解释它们——它是我们今天在 Web 上的唯一基线。
职责分离
WebGPU 与 WebGL 之间的第一个重要区别在于 WebGPU 将资源管理、工作准备和提交到 GPU (图形处理单元) 分开。在 WebGL 中,单个上下文对象负责所有内容,并且包含许多关联的状态。相反,WebGPU 将它们分成多个不同的上下文
GPUDevice
创建资源,例如纹理和缓冲区。GPUCommandEncoder
允许编码单个命令,包括渲染和计算传递。- 完成后,它将变成
GPUCommandBuffer
对象,可以提交到GPUQueue
以在 GPU 上执行。 - 我们可以将渲染结果呈现到 HTML 画布。或者多个画布。或者根本没有画布——使用纯粹的计算工作流。
总的来说,这种分离将允许 Web 上的复杂应用程序在一种或多种 工作线程 中流式传输数据并动态创建与其关联的任何 GPU 资源。同时,同一个应用程序可以在多个工作线程上记录工作,并最终将所有工作一起提交到 GPUQueue
。这与原生图形密集型应用程序的多线程场景相匹配,并允许高度利用多核处理器。
管道状态
第二个重要变化是 WebGPU 如何封装管道状态。
在 WebGL 中,用户将在程序初始化时创建一个 着色器 程序。当用户后来尝试使用此着色器程序时,驱动程序会考虑当前设置的所有其他状态,并且可能需要在内部重新编译着色器程序。如果驱动程序确实重新编译了着色器程序,这可能会导致 CPU 停顿。
相反,WebGPU 有一个管道状态对象的概念(即 GPURenderPipeline
和 GPUComputePipeline
)。管道状态对象是用户预先在设备上创建的各种状态的组合——就像在原生 API 中一样。用户预先提供所有这些状态,这使浏览器和硬件驱动程序能够在以后在 GPU 操作中使用它时避免额外的工作(例如着色器重新编译)。
从开发人员的角度来看,管理这些粗粒度状态对象也更容易。他们不必过多地考虑要更改哪些细粒度状态以及要保留哪些状态。
管道状态包括
- 着色器
- 顶点缓冲区和属性的布局
- 绑定组的布局
- 混合、深度和模板状态
- 输出渲染目标的格式
绑定模型
WebGPU 与 WebGL 之间的第三个区别是绑定模型。WebGPU 绑定模型很大程度上受到 Vulkan 的启发(作为目标原生 API 功能的交集),并允许将资源分组到 GPUBindGroup
对象中。然后,我们在命令记录期间绑定 GPUBindGroup
,以便在着色器中使用这些资源。
通过预先创建这些绑定组,图形驱动程序可以提前执行任何必要的准备。这使浏览器能够在绘图调用之间更快地更改资源绑定。
最重要的是,用户必须提前描述资源绑定的布局,将其烘焙到 GPUBindGroupLayout
对象中。管道状态和具体绑定组也知道绑定组布局。此知识充当着色器和 API 之间的契约。它使浏览器或驱动程序能够以一种允许更快绑定的方式布置资源。
补充演讲
标准组的活跃成员有公开演讲,这可能有助于更好地理解 API 是什么,它是如何演变的,以及我们期望它如何使用。来自 Mozilla 的 Dzmitry Malyshau 在 Fosdem 2020 上谈到了为原生平台实现 WebGPU 的 Rust 方面。早些时候,来自 Google 的 Corentin Wallez 在 DevFest Toulouse 2019 上概述了 WebGPU API。最后但并非最不重要的是,Google I/O 2019 演示文稿充满了闪亮 的演示和代码示例。
Firefox 故事
技术栈
在 Firefox 中,我们正在努力实现 WebGPU 规范的完整从头开始的实现。核心逻辑由 wgpu-core 项目提供,该项目由 Rust 社区在 Mozilla 的帮助下编写。它基于 gfx-rs 项目,该项目能够将类似 Vulkan 的 GPU 工作负载转换为 D3D12、D3D11、Metal,甚至 OpenGL(在一定程度上)。
我们还在努力开发 着色器基础设施,它将使我们能够使用 WebGPU 着色语言,根据 API 期望对其进行验证,并将其转换为驱动程序期望的后端着色语言。
我们最新的工作可以在 Nightly 中看到,并设置了“dom.webgpu.enabled = true
”首选项,这也需要“gfx.webrender.all = true
”。它应该在具有 Vulkan 驱动的 Windows 7 和 Linux、Windows 10、macOS 以及合格的 Android 设备上运行。请做好遇到颠簸的准备,因为所有内容都还在开发中!
示例
在撰写本文时,Firefox Nightly 可以运行所有 Google 的 基于 SPIR-V 的 WebGPU 示例,但“animometer”(它依赖于我们尚未实现的 GPURenderBundle
)除外。以下是 Nightly 在 Linux/Vulkan 上渲染“分形立方体”
我们还可以执行计算工作负载。例如,以下是 Nightly 在 Windows 10/Vulkan 上渲染“鸟群”示例
Rust 社区也一直在努力直接针对 WebGPU 进行工作,在 wgpu-rs 中(它提供了一个 Rust API 并使用同一个 wgpu 项目来实现它)。这项激动人心的工作为我们在 Rust 生态系统中拥有许多现有应用程序在浏览器中运行打开了大门。这些应用程序的第一批是 wgpu-rs
的 自身示例。以下是 Nightly 在 macOS/Metal 上渲染“阴影”示例
未来工作
Firefox Nightly 中还有很多东西缺失,才能使这个 WebGPU 实现真正可用。目前还处于早期阶段,我们只是让第一个示例工作。
错误处理
一个主要缺失的领域是错误模型。WebGPU 错误基于“传染性内部可空性”(也称为“maybe monad”)的概念:在某个时刻,一个使用不当的对象可能会变成“无效”。实际错误将异步返回到内容端。如果任何其他对象变得依赖于它(例如,纹理作为绑定组对象的一部分),那么该父对象也会变成“无效”——因此状态是传染性的。实现此错误模型将使开发人员能够在不崩溃 GPU 进程(这通常是安全的 Rust 恐慌)或造成任何其他副作用的情况下迭代代码。
加速呈现
另一个需要实现的重要部分是硬件对呈现的支持。目前,要将渲染的图像显示到 HTML 画布,该图像首先在 GPU 上渲染。然后将其读回 CPU 端缓冲区,我们将其提供给 WebRender 作为“外部图像”。WebRender 将图像内容再次上传到 GPU,最后将其显示在 HTML 画布上。这种往返通常是不必要的。相反,我们希望使用 WebGPU 后端 API 和 WebRender 之间表面共享的特定于平台的机制。我们将需要独立地针对每个平台实现这一点,同时保留当前路径作为后备。
API 覆盖范围
最后,规范中还有一些我们尚未实现的部分。随着规范的不断发展,我们将持续跟进,直到最终稳定。其中一个缺失的部分是对GPURenderBundle
对象的支持。每个捆绑包包含一个小的渲染命令序列,它可以在录制通道中多次使用和重用。目前,它是 API 中唯一可以重用命令的机制。对于 Web 上更复杂的内容,例如开放世界游戏,它将非常重要。
规范状态
WebGPU 规范 在 GitHub 上以 Bikeshed 文档的形式开发(主 WebGPU 规范和 WebGPU 着色语言分开)。欢迎参与!
该小组已基本解决了 API 的主要架构问题。最近,我们根据 Tint 原型 达成了 WebGPU 着色语言方向的共识。在允许最终用户使用它编写着色器之前,我们还需要解决很多设计难题。
一个未解决的问题是 CPU 和 GPU 之间数据传输的 API。直接处理内存是 Web 平台与原生平台有很大区别的地方。我们讨论了十几个不同的方案,但 尚未找到 符合我们原则的设计解决方案。
总的来说,规范仍然处于开发阶段。它可供早期黑客使用,但不建议在生产环境中使用。我们希望在 2020 年年底之前获得规范和实现的最小可行产品版本。可以在 webgpu.io 上查看实现的当前状态。
关于 Dzmitry Malyshau
Dzmitry 是一位资深的 Rustacean,曾从事 AAA 游戏开发。他于 2016 年加入 Mozilla,帮助将 WebRender 集成到 Gecko 中,并与所有浏览器供应商合作设计新的 WebGPU API。
12 条评论