从 WebGL 到 WebGPU

Number of views 103

作为 WebGL 开发者,您可能会对开始使用 WebGPU 感到既紧张又兴奋。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内存的成本,以及使用worker和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确保了应用程序能在尽可能多的设备上运行。

Canvas处理

在创建了一个WebGL上下文并提供了诸如alpha、antialias、colorSpace、depth、preserveDrawingBuffer或stencil等上下文属性后,WebGL会自动管理canvas。

相比之下,WebGPU要求你自己管理canvas。例如,在WebGPU中要实现抗锯齿,你需要创建一个多采样纹理并渲染到该纹理上。然后,将多采样纹理解析为常规纹理,并将该纹理绘制到canvas上。这种手动管理允许你从单个GPUDevice对象输出到任意数量的canvases。而WebGL只能为每个canvas创建一个上下文。

可以查看WebGPU多个Canvas示例演示来了解更多。

另外需要注意的是,目前浏览器对每页的WebGL canvases数量有限制。在撰写本文时,Chrome和Safari同时最多只能使用16个WebGL canvases;Firefox则最多可以创建200个。另一方面,对于每页的WebGPU canvases数量则没有限制。这表明WebGPU在处理多个输出目标方面具有更大的灵活性和潜力。

image1738806364728.png

有用的错误信息

WebGPU为API返回的每条消息提供了一个调用栈,这意味着你可以快速查看代码中错误发生的位置,这有助于调试和修复错误。除了提供调用栈外,WebGPU的错误信息还易于理解并可操作,通常包括错误描述及如何修复错误的建议。此外,WebGPU允许你为每个WebGPU对象提供自定义标签,该标签会在GPUError消息、控制台警告和浏览器开发者工具中使用。

从名称到索引

在WebGL中,许多东西通过名称连接在一起。例如,可以在GLSL中声明一个名为myUniform的一致变量,并使用gl.getUniformLocation(program, 'myUniform')获取其位置。而在WebGPU中,所有内容完全通过字节偏移量或索引(常被称为location)连接。你需要自行确保WGSL代码和JavaScript中的位置同步。

Mipmap生成

在WebGL中,可以创建纹理的0级mip,然后调用gl.generateMipmap()让WebGL为你生成其他mip级别。但在WebGPU中,必须自己生成mipmaps,没有内置函数来完成这项工作。可以使用如webgpu-utils这样的便利库来生成mipmaps或学习如何自己实现。

存储缓冲区和存储纹理

WebGL和WebGPU都支持一致缓冲区(Uniform buffers),允许向着色器传递有限大小的常量参数。而WebGPU支持的存储缓冲区 (Storage buffers)功能更强大灵活,可以传递比一致缓冲区大得多的数据。存储缓冲区是可写的,并支持一些原子操作,而一致缓冲区只读。

  • 存储缓冲区传递给着色器的数据可以比一致缓冲区大得多。虽然规范指出一致缓冲区绑定的最大尺寸可达64KB(参见 maxUniformBufferBindingSize),但在WebGPU中,存储缓冲区绑定的最大尺寸至少为128MB(参见 maxStorageBufferBindingSize)。
  • 存储缓冲区是可写的,并支持一些原子操作,而一致缓冲区仅可读。这一点允许实现新的算法类别,这些算法可能需要对数据进行修改或执行复杂的同步操作。
  • 此外,存储缓冲区绑定支持运行时确定数组大小,这使得算法更加灵活。相比之下,一致缓冲区的数组大小必须在着色器中预先定义。这意味着使用存储缓冲区时,可以根据程序的实际需求动态调整数据结构的大小,而不必事先确定所有细节,从而提供了更高的灵活性和效率。这对于处理那些需要动态数据结构或大规模数据集的应用场景尤为重要。

存储纹理(Storage textures)仅存在于WebGPU中,与存储缓冲区相对于一致缓冲区的关系类似,它们比普通纹理更加灵活,支持随机访问写入(未来也可能支持读取)。

缓冲区和纹理的变化

在WebGL中,可以随时使用gl.bufferData()gl.texImage2D()等方法改变缓冲区或纹理的大小。然而,在WebGPU中,缓冲区和纹理是不可变的,即创建后不能更改其大小、用途或格式,只能修改其内容。

空间约定差异

在WebGL中,Z剪裁空间范围是从-1到1;而在WebGPU中,Z剪裁空间范围是从0到1。这意味着z值为0的对象离摄像机最近,而z值为1的对象离得最远。这些差异需要注意,以确保正确地渲染3D场景。

image1738807518666.png

0 Answers