作为 WebGL 开发者,您可能既感到畏惧,又很兴奋,因为 WebGPU 是 WebGL 的继任者,它将现代图形 API 的进步带到了 Web 上。
令人欣慰的是,WebGL 和 WebGPU 有许多核心概念是相同的。这两个 API 都允许您在 GPU 上运行称为着色器的小程序。WebGL 支持顶点着色器和片段着色器,而 WebGPU 还支持计算着色器。WebGL 使用 OpenGL 着色语言 (GLSL),而 WebGPU 使用 WebGPU 着色语言 (WGSL)。虽然这两种语言不同,但基本概念大致相同。
鉴于此,本文重点介绍了 WebGL 和 WebGPU 之间的一些差异,以帮助您入门。
全局状态
WebGL 具有大量全局状态。某些设置适用于所有渲染操作,例如绑定了哪些纹理和缓冲区。您可以通过调用各种 API 函数来设置此全局状态,并且此状态会一直有效,直到您更改它为止。WebGL 中的全局状态是错误的主要来源,因为很容易忘记更改全局设置。此外,全局状态会使代码共享变得困难,因为开发者需要小心谨慎,以免意外更改全局状态,从而影响代码的其他部分。
WebGPU 是一种无状态 API,不维护全局状态。而是使用流水线的概念来封装 WebGL 中全局的所有渲染状态。流水线包含要使用的混合、拓扑和属性等信息。流水线是不可变的。如果您想更改某些设置,则需要创建另一个流水线。WebGPU 还使用命令编码器将命令批量处理在一起,并按记录顺序执行这些命令。例如,在阴影贴图中,此功能非常有用,因为应用可以在对对象进行单次遍历时记录多个命令流,每个命令流对应一个光源的阴影贴图。
总而言之,由于 WebGL 的全局状态模型使得创建稳健、可组合的库和应用变得困难且脆弱,因此 WebGPU 大幅减少了开发者在向 GPU 发送命令时需要跟踪的状态量。
不再同步
在 GPU 上,同步发送命令并等待命令完成通常效率低下,因为这会清空流水线并导致气泡。在 WebGPU 和 WebGL 中尤其如此,它们使用多进程架构,其中 GPU 驱动程序在与 JavaScript 不同的进程中运行。
例如,在 WebGL 中,调用 gl.getError()
需要从 JavaScript 进程到 GPU 进程再返回的同步 IPC。由于这两个进程会进行通信,因此可能会在 CPU 端产生气泡。
为避免这些气泡,WebGPU 设计为完全异步。错误模型和所有其他操作都是异步进行的。例如,当您创建纹理时,即使纹理实际上存在错误,该操作也会立即显示为成功。您只能异步发现错误。此设计可确保跨进程通信顺畅无阻,并为应用提供可靠的性能。
计算着色器
计算着色器是在 GPU 上运行的程序,用于执行通用计算。它们仅在 WebGPU 中可用,在 WebGL 中不可用。
与顶点着色器和片段着色器不同,计算着色器不仅限于图形处理,还可用于各种任务,例如机器学习、物理模拟和科学计算。计算着色器由数百甚至数千个线程并行执行,因此非常适合处理大型数据集。如需了解 GPU 计算,请参阅这篇关于 WebGPU 的详尽文章。
视频帧处理
使用 JavaScript 和 WebAssembly 处理视频帧存在一些缺点:将数据从 GPU 内存复制到 CPU 内存的成本,以及通过工作器和 CPU 线程实现的有限并行性。WebGPU 没有这些限制,因此非常适合处理视频帧,这得益于它与 WebCodecs API 的紧密集成。
以下代码段展示了如何在 WebGPU 中将 VideoFrame 作为外部纹理导入并进行处理。您可以试用此演示。
// Init WebGPU device and pipeline...
// Configure canvas context...
// Feed camera stream to video...
(function render() {
const videoFrame = new VideoFrame(video);
applyFilter(videoFrame);
requestAnimationFrame(render);
})();
function applyFilter(videoFrame) {
const texture = device.importExternalTexture({ source: videoFrame });
const bindgroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: texture }],
});
// Finally, submit commands to GPU
}
默认应用可移植性
WebGPU 强制您请求 limits
。默认情况下,requestDevice()
返回的 GPUDevice 可能与物理设备的硬件功能不匹配,而是所有 GPU 的合理最低公分母。通过要求开发者请求设备限制,WebGPU 可确保应用在尽可能多的设备上运行。
画布处理
创建 WebGL 上下文并提供 context 属性(例如 alpha、antialias、colorSpace、depth、preserveDrawingBuffer 或 stencil)后,WebGL 会自动管理画布。
另一方面,WebGPU 要求您自行管理画布。例如,为了在 WebGPU 中实现抗锯齿,您需要创建一个多重采样纹理并渲染到该纹理。然后,您会将多重采样纹理解析为常规纹理,并将该纹理绘制到画布上。通过这种手动管理,您可以从单个 GPUDevice 对象输出到任意数量的画布。相比之下,WebGL 只能为每个画布创建一个上下文。
不妨查看 WebGPU 多个画布演示。
另请注意,浏览器目前对每个网页的 WebGL 画布数量有限制。在撰写本文时,Chrome 和 Safari 最多只能同时使用 16 个 WebGL 画布;Firefox 最多可以创建 200 个。另一方面,每个网页的 WebGPU 画布数量没有限制。

实用错误消息
WebGPU 为从 API 返回的每条消息提供调用堆栈。这意味着您可以快速查看代码中发生错误的位置,这对于调试和修复错误非常有用。
除了提供调用堆栈之外,WebGPU 错误消息还易于理解且可作为操作依据。错误消息通常包含错误说明以及有关如何修复错误的建议。
WebGPU 还允许您为每个 WebGPU 对象提供自定义 label
。然后,浏览器会在 GPUError 消息、控制台警告和浏览器开发者工具中使用此标签。
从名称到索引
在 WebGL 中,许多事物都是通过名称关联的。例如,您可以在 GLSL 中声明一个名为 myUniform
的统一变量,并使用 gl.getUniformLocation(program, 'myUniform')
获取其位置。如果您错误地输入了统一变量的名称,系统会显示错误,因此这非常有用。
另一方面,在 WebGPU 中,所有内容都完全通过字节偏移量或索引(通常称为“位置”)连接。您有责任确保 WGSL 和 JavaScript 中代码的位置保持同步。
mipmap 生成
在 WebGL 中,您可以创建纹理的 0 级 mip,然后调用 gl.generateMipmap()
。然后,WebGL 将为您生成所有其他 mip 级别。
在 WebGPU 中,您必须自行生成 mipmap。没有内置函数可以实现此目的。如需详细了解此决定,请参阅规范讨论。您可以使用 webgpu-utils 等实用库来生成 mipmap,也可以了解如何自行生成。
存储缓冲区和存储纹理
WebGL 和 WebGPU 都支持 uniform 缓冲区,可让您将大小有限的常量形参传递给着色器。存储缓冲区与 uniform 缓冲区非常相似,但仅受 WebGPU 支持,并且比 uniform 缓冲区更强大、更灵活。
传递给着色器的存储缓冲区数据可以比统一缓冲区大得多。虽然规范规定统一缓冲区绑定的大小上限为 64KB(请参阅
maxUniformBufferBindingSize
),但存储缓冲区绑定的最大大小在 WebGPU 中至少为 128MB(请参阅maxStorageBufferBindingSize
)。存储缓冲区是可写入的,并支持一些原子操作,而统一缓冲区只是只读的。这样就可以实现新的算法类别。
存储缓冲区绑定支持运行时大小的数组,以实现更灵活的算法,而 uniform 缓冲区数组大小必须在着色器中提供。
存储纹理仅在 WebGPU 中受支持,它与纹理的关系类似于存储缓冲区与统一缓冲区的关系。它们比常规纹理更灵活,支持随机访问写入(未来还将支持读取)。
缓冲区和纹理更改
在 WebGL 中,您可以创建缓冲区或纹理,然后随时使用 gl.bufferData()
和 gl.texImage2D()
分别更改其大小。
在 WebGPU 中,缓冲区和纹理是不可变的。也就是说,这些磁盘在创建后无法更改其大小、用途或格式。您只能更改其内容。
空格使用惯例差异
在 WebGL 中,Z 剪裁空间范围为 -1 到 1。在 WebGPU 中,Z 裁剪空间范围为 0 到 1。这意味着,z 值为 0 的对象离相机最近,而 z 值为 1 的对象离相机最远。
WebGL 采用 OpenGL 惯例,其中 Y 轴向上,Z 轴朝向观看者。WebGPU 使用 Metal 惯例,其中 Y 轴向下,Z 轴垂直于屏幕向外。请注意,在帧缓冲区坐标、视口坐标和片段/像素坐标中,Y 轴方向向下。在剪辑空间中,Y 轴方向仍与 WebGL 中一样向上。
致谢
感谢 Corentin Wallez、Gregg Tavares、Stephen White、Ken Russell 和 Rachel Andrew 审核本文。
我还建议访问 WebGPUFundamentals.org,深入了解 WebGPU 和 WebGL 之间的差异。