WebGPU:解锁浏览器对新式 GPU 的访问权限

了解 WebGPU 如何释放 GPU 的强大能力,以实现更快的机器学习性能和更出色的图形渲染效果。

Corentin Wallez
Corentin Wallez
François Beaufort
François Beaufort

新的 WebGPU API 可显著提升图形和机器学习工作负载的性能。本文探讨了 WebGPU 如何改进当前的 WebGL 解决方案,并抢先了解了未来的发展。不过,我们先来了解一下 WebGPU 的开发背景。

WebGPU 上的上下文

WebGL 于 2011 年在 Chrome 中推出。WebGL 可让 Web 应用利用 GPU,从而为 Web 带来惊艳的体验,包括 Google 地球、互动式音乐视频和 3D 房地产演示等等。WebGL 基于 1992 年首次开发的 OpenGL 系列 API。那是很久以前的事了!您可以想象,自那时起,GPU 硬件已经有了显著进步。

为了跟上这一发展步伐,我们开发了一个新的 API,可以更高效地与现代 GPU 硬件交互。Direct3D 12MetalVulkan 等 API。这些新的 API 为 GPU 编程的新应用场景提供了支持,例如机器学习的爆炸和渲染算法的进步。WebGPU 是 WebGL 的后继者,将这类新型现代 API 的进步带到了 Web 上。

WebGPU 为在浏览器中进行 GPU 编程提供了许多新的可能性。它更好地反映了现代 GPU 硬件的工作方式,同时也为将来更高级的 GPU 功能奠定了基础。自 2017 年以来,此 API 一直在 W3C 的“Web GPU”群组中推出,是 Apple、Google、Mozilla、Microsoft 和 Intel 等众多公司共同努力的成果。经过 6 年的工作,我们很高兴地宣布,网络平台的重大功能之一终于推出了!

目前,Chrome 113 在 ChromeOS、macOS 和 Windows 上支持 WebGPU,其他平台也即将推出。衷心感谢其他 Chromium 贡献者和 Intel 团队,特别感谢帮助我们实现这一愿景。

现在,我们来了解 WebGPU 能够实现的一些激动人心的使用场景。

解锁新的 GPU 渲染工作负载

计算着色器等 WebGPU 功能支持将新类算法移植到 GPU 上。例如,可以为场景添加更多动态细节、模拟物理现象等的算法!甚至有些工作负载以前只能使用 JavaScript 完成,现在也可以移至 GPU。

以下视频展示了如何使用 marching cubes 算法对这些元球的表面进行三角剖分。在视频的前 20 秒内,当使用 JavaScript 时,算法无法跟上页面只以 8 FPS 的速度运行,从而导致动画出现卡顿。为了在 JavaScript 中保持其性能,我们需要大幅降低细节级别。

将相同的算法移至计算着色器后,效果天差地别,如视频中 20 秒后所示。性能显著提升,页面现在以流畅的 60 FPS 运行,并且仍有大量性能余量可用于其他效果。此外,页面的主 JavaScript 循环会完全释放出来用于执行其他任务,从而确保与页面的互动始终保持响应。

“Metaballs”演示

WebGPU 还可以实现以前无法实现的复杂视觉效果。在以下示例中,我们使用热门的 Babylon.js 库创建了海洋表面,该表面完全在 GPU 上进行模拟。逼真的动态效果是通过将许多独立的波叠加在一起而产生的。但是,直接模拟每个波浪的费用太高。

海洋演示

正因如此,本演示使用了名为 Fast Fourier Transform 的高级算法。这种方法使用的是谱数据,而不是将所有波浪都表示为复杂的位置数据,因此在执行计算时效率更高。然后,每个帧都会使用傅里叶转换将光谱数据转换为表示波浪高度的位置数据。

更快的机器学习推理

WebGPU 还可用于加速机器学习,这已成为近年来 GPU 的主要用途。

长久以来,富有创造力的开发者一直在将 WebGL 的渲染 API 用于执行非渲染操作,例如机器学习计算。不过,这需要通过绘制三角形的像素来启动计算,并在纹理中仔细打包和解压缩张量数据,而不是使用更通用的内存访问方式。

一张插图,展示了使用 WebGL 执行单个 ML 运算符时存在的低效问题,包括冗余的内存加载、冗余的计算以及每个线程写入的值较少。
使用 WebGL 执行单个 ML 运算。

以这种方式使用 WebGL 需要开发者将其代码笨拙地调整为符合仅用于绘图的 API 的预期。再加上计算之间缺少共享内存访问等基本功能,会导致重复工作和性能欠佳。

计算着色器是 WebGPU 的主要新功能,可以消除这些痛点。计算着色器提供了更灵活的编程模型,可充分利用 GPU 的大规模并行特性,同时不受渲染操作的严格结构的约束。

WebGPU 计算着色器中的各种效率提升,包括共享内存加载、共享计算和对内存的灵活写入。
WebGPU 计算着色器效率。

计算着色器可让您在着色器工作组内共享数据和计算结果,从而提高效率。与之前尝试出于相同目的使用 WebGL 相比,这可以带来显著的改进。

举个例子来说明这可以带来多大的效率提升:在 TensorFlow.js 中将图像扩散模型的初始移植从 WebGL 迁移到 WebGPU 后,在各种硬件上性能提升了 3 倍。经过测试,在一些硬件上,图片的渲染时间不到 10 秒。而且,由于这是一个早期的端口,我们相信 WebGPU 和 TensorFlow.js 都可以得到更多改进!不妨参阅 2023 年 Web 机器学习领域的新进展Google I/O 大会专题演讲。

不过,WebGPU 不仅仅是将 GPU 功能引入到 Web 中。

专为 JavaScript 而设计

支持这些用例的功能已经面向特定平台的桌面版和移动版开发者推出了一段时间,而我们面临的挑战是,如何以一种感觉像是 Web 平台的自然组成部分的方式来提供这些功能。

WebGPU 的开发借鉴了十多年来使用 WebGL 进行出色工作的开发者的后见之明。我们能够将他们遇到的问题、遇到的瓶颈和提出的问题纳入他们,并将所有反馈都集中到这个新的 API 中。

我们发现,WebGL 的全局状态模型使得创建可组合且强大的库和应用变得困难且脆弱。因此,WebGPU 大大减少了开发者在发送 GPU 命令时需要跟踪的状态量。

我们了解到,调试 WebGL 应用很麻烦,因此 WebGPU 包含更灵活的错误处理机制,不会降低性能。我们竭尽全力确保您从 API 返回的每条消息都易于理解且可操作

我们还发现,频繁进行过多 JavaScript 调用的开销是复杂 WebGL 应用的瓶颈。因此,WebGPU API 的通信量较少,因此您可以通过更少的函数调用完成更多工作。我们专注于预先执行重量级验证,尽可能使关键绘制循环保持精简。我们还提供了 Render Bundle 等新 API,可让您预先记录大量绘制命令,并通过单次调用重放这些命令。

为了演示渲染软件包等功能可以产生的巨大差异,下面是 Babylon.js 中的另一个演示。他们的 WebGL 2 渲染程序每秒可以执行大约 500 次 JavaScript 调用来渲染此艺术画廊场景。太棒了!

美术馆

不过,他们的 WebGPU 渲染程序启用了一种称为“快照渲染”的功能。此功能基于 WebGPU 渲染软件包构建,可将提交相同场景的速度提高 10 倍以上。这显著降低了开销,使得 WebGPU 能够渲染更复杂的场景,同时让应用能够并行使用 JavaScript 执行更多操作。

现代图形 API 以复杂著称,它们会以牺牲简单性为代价,提供极佳的优化机会。另一方面,WebGPU 专注于跨平台兼容性,在大多数情况下,会自动处理资源同步等传统上难以处理的主题。

这有一个令人愉快的副作用,即 WebGPU 易于学习和使用。它依赖于 Web 平台的现有功能来执行图片和视频加载等操作,并采用了 promise 等众所周知的 JavaScript 模式来执行异步操作。这有助于将所需的样板代码量保持在最低限度。只需不到 50 行代码,就能在屏幕上显示第一个三角形。

<canvas id="canvas" width="512" height="512"></canvas>
<script type="module">
  const adapter = await navigator.gpu.requestAdapter();
  const device = await adapter.requestDevice();

  const context = canvas.getContext("webgpu");
  const format = navigator.gpu.getPreferredCanvasFormat();
  context.configure({ device, format });

  const code = `
    @vertex fn vertexMain(@builtin(vertex_index) i : u32) ->
      @builtin(position) vec4f {
       const pos = array(vec2f(0, 1), vec2f(-1, -1), vec2f(1, -1));
       return vec4f(pos[i], 0, 1);
    }
    @fragment fn fragmentMain() -> @location(0) vec4f {
      return vec4f(1, 0, 0, 1);
    }`;
  const shaderModule = device.createShaderModule({ code });
  const pipeline = device.createRenderPipeline({
    layout: "auto",
    vertex: {
      module: shaderModule,
      entryPoint: "vertexMain",
    },
    fragment: {
      module: shaderModule,
      entryPoint: "fragmentMain",
      targets: [{ format }],
    },
  });
  const commandEncoder = device.createCommandEncoder();
  const colorAttachments = [
    {
      view: context.getCurrentTexture().createView(),
      loadOp: "clear",
      storeOp: "store",
    },
  ];
  const passEncoder = commandEncoder.beginRenderPass({ colorAttachments });
  passEncoder.setPipeline(pipeline);
  passEncoder.draw(3);
  passEncoder.end();
  device.queue.submit([commandEncoder.finish()]);
</script>

总结

很高兴看到 WebGPU 为 Web 平台带来的所有新的可能性,我们也期待看到 WebGPU 带来的所有炫酷新用例!

围绕 WebGL 构建了一个充满活力的库和框架生态系统,而这个生态系统也非常乐意拥抱 WebGPU。许多热门 JavaScript WebGL 库正在或已完成 WebGPU 支持,在某些情况下,只需更改一个标志,即可获享 WebGPU 的优势!

Babylon.js、Construct 3、Google 地球、Google Meet、PlayCanvas、Sketchfab、Three.JS、TensorFlow.js 和 Unity。
已完成或正在进行 WebGPU 移植的框架、应用和库。

这只是 Chrome 113 中的第一个版本,我们还会继续努力。虽然我们的初始版本适用于 Windows、ChromeOS 和 MacOS,但我们计划在不久的将来为 Android 和 Linux 等其他平台引入 WebGPU。

不仅 Chrome 团队也在努力推出 WebGPU,Firefox 和 WebKit 也在实现中。

此外,W3C 已经在设计新功能,这些功能将在硬件上推出时公开。例如:我们计划在 Chrome 中很快启用对着色器中的 16 位浮点数的支持DP4a 类指令,以进一步提升机器学习性能。

WebGPU 是一个功能丰富的 API,如果您投入精力,可以实现出色的性能。今天,我们只能大致介绍 WebGPU 的优势,但如果您想开始使用 WebGPU,请查看我们的入门 Codelab:您的首个 WebGPU 应用。在此 Codelab 中,您将构建经典的 Conway 生命游戏的 GPU 版本。此 Codelab 会逐步引导您完成该过程,即使您是首次进行 GPU 开发,也可以尝试一下。

WebGPU 示例也是了解该 API 的好方法。这些示例包括传统的“Hello Triangle”示例,以及更完整的渲染和计算流水线,展示了各种技术。最后,请查看我们的其他资源