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 年的工作,我们很高兴地宣布,网络平台的重大功能之一终于推出了!

目前,ChromeOS、macOS 和 Windows 上的 Chrome 113 已支持 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 上进行模拟。逼真的动态效果是通过将许多独立的波叠加在一起而产生的。但是,直接模拟每个波浪的费用太高。

海洋演示

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

更快的机器学习推理

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,但我们计划在不久的将来将 WebGPU 引入 Android 和 Linux 等其他平台。

而且,不仅仅是 Chrome 团队一直在致力于推出 WebGPU。Firefox 和 WebKit 也在实现中。

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

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

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