WebGPU API

实验性: 这是一个 实验性技术
在生产环境中使用此功能前,请仔细查看 浏览器兼容性表格

安全上下文:此功能仅在 安全上下文(HTTPS)中可用,并且在某些或所有 支持的浏览器 中可用。

WebGPU API 使 Web 开发人员能够使用底层系统的 GPU(图形处理单元)执行高性能计算,并绘制可以在浏览器中呈现的复杂图像。

WebGPU 是 WebGL 的继任者,它提供了更好的现代 GPU 兼容性、对通用 GPU 计算的支持、更快的操作以及对更高级 GPU 功能的访问。

概念和用法

可以公平地说,WebGL 在大约 2011 年首次出现后,在图形功能方面彻底改变了 Web。WebGL 是 OpenGL ES 2.0 图形库的 JavaScript 移植版本,允许网页将渲染计算直接传递到设备的 GPU 以进行高速处理,并在 <canvas> 元素内呈现结果。

WebGL 和用于编写 WebGL 着色器代码的 GLSL 语言都很复杂,因此创建了几个 WebGL 库来简化 WebGL 应用程序的编写:流行的示例包括 Three.jsBabylon.jsPlayCanvas。开发人员使用这些工具构建了沉浸式的基于 Web 的 3D 游戏、音乐视频、培训和建模工具、VR 和 AR 体验等。

但是,WebGL 存在一些需要解决的基本问题。

  • 自 WebGL 发布以来,出现了一代新的原生 GPU API——最流行的是 Microsoft 的 Direct3D 12Apple 的 MetalKhronos Group 的 Vulkan——它们提供了大量新功能。OpenGL(以及 WebGL)不再计划更新,因此它不会获得任何这些新功能。另一方面,WebGPU 将在未来添加新功能。
  • WebGL 完全基于绘制图形并将其渲染到画布的用例。它不能很好地处理通用 GPU (GPGPU) 计算。GPGPU 计算对于许多不同的用例变得越来越重要,例如基于机器学习模型的用例。
  • 3D 图形应用程序在同时渲染的对象数量和新渲染功能的使用方面变得越来越苛刻。

WebGPU 解决了这些问题,提供了一个与现代 GPU API 兼容的更新的通用架构,感觉更“Web 化”。它支持图形渲染,但也为 GPGPU 计算提供了第一类支持。单个对象的渲染在 CPU 端的成本大大降低,并且它支持现代 GPU 渲染功能,例如基于计算的粒子以及颜色效果、锐化和景深模拟等后期处理滤镜。此外,它还可以直接在 GPU 上处理诸如剔除和蒙皮模型变换等昂贵的计算。

通用模型

在设备 GPU 和运行 WebGPU API 的 Web 浏览器之间存在多层抽象。在开始学习 WebGPU 时,了解这些层很有用。

A basic stack diagram showing the position of the different elements of a WebGPU architecture on a device

  • 物理设备具有 GPU。大多数设备只有一个 GPU,但有些设备有多个 GPU。提供了不同的 GPU 类型。
    • 集成 GPU,它与 CPU 位于同一电路板上并共享其内存。
    • 独立 GPU,它位于自己的电路板上,与 CPU 分开。
    • 软件“GPU”,在 CPU 上实现。

    注意:上图假设设备只有一个 GPU。

  • 原生 GPU API 是操作系统的一部分(例如 macOS 上的 Metal),它是一个编程接口,允许原生应用程序使用 GPU 的功能。API 指令通过驱动程序发送到 GPU(并接收响应)。系统可能有多个可用于与 GPU 通信的原生操作系统 API 和驱动程序,尽管上图假设设备只有一个原生 API/驱动程序。
  • 浏览器的 WebGPU 实现通过原生 GPU API 驱动程序处理与 GPU 的通信。在您的代码中,WebGPU 适配器有效地表示底层系统上可用的物理 GPU 和驱动程序。
  • 逻辑设备是一个抽象,通过它单个 Web 应用程序可以以分隔的方式访问 GPU 功能。逻辑设备是必需的,以提供多路复用功能。物理设备的 GPU 由许多应用程序和进程同时使用,包括可能存在的许多 Web 应用程序。出于安全和逻辑原因,每个 Web 应用程序都需要能够独立访问 WebGPU。

访问设备

逻辑设备(由 GPUDevice 对象实例表示)是 Web 应用程序访问所有 WebGPU 功能的基础。访问设备的操作如下:

  1. Navigator.gpu 属性(如果您从工作线程内部使用 WebGPU 功能,则为 WorkerNavigator.gpu)返回当前上下文的 GPU 对象。
  2. 您可以通过 GPU.requestAdapter() 方法访问适配器。此方法接受一个可选的设置对象,允许您请求例如高性能或低功耗适配器。如果没有包含此设置,则设备将提供对默认适配器的访问,这对于大多数用途来说已经足够了。
  3. 可以通过 GPUAdapter.requestDevice() 请求设备。此方法也接受一个选项对象(称为描述符),可用于指定您希望逻辑设备具有的确切功能和限制。如果没有包含此设置,则提供的设备将具有合理的通用规范,这对于大多数用途来说已经足够了。

将此与一些功能检测检查结合起来,上述过程可以如下实现:

js
async function init() {
  if (!navigator.gpu) {
    throw Error("WebGPU not supported.");
  }

  const adapter = await navigator.gpu.requestAdapter();
  if (!adapter) {
    throw Error("Couldn't request WebGPU adapter.");
  }

  const device = await adapter.requestDevice();

  //...
}

管道和着色器:WebGPU 应用程序结构

管道是一种逻辑结构,包含一系列可编程阶段,这些阶段完成后即可完成程序的工作。WebGPU 目前能够处理两种类型的管道:

  • 渲染管道将图形渲染到 <canvas> 元素中,但它也可以将图形渲染到屏幕外。它有两个主要阶段:
    • 顶点阶段,其中顶点着色器获取馈送到 GPU 的位置数据,并通过应用旋转、平移或透视等指定效果将其用于在 3D 空间中定位一系列顶点。然后,顶点被组装成诸如三角形(渲染图形的基本构建块)之类的图元,并由 GPU 进行光栅化,以确定每个图元应该在绘图画布上覆盖哪些像素。
    • 片段阶段,其中片段着色器计算顶点着色器生成的图元覆盖的每个像素的颜色。这些计算经常使用诸如图像(以纹理形式)之类的输入,这些输入提供表面细节以及虚拟光源的位置和颜色。
  • 计算管道用于通用计算。计算管道包含一个计算阶段,其中计算着色器获取通用数据,在指定数量的工作组中并行处理它,然后将结果返回到一个或多个缓冲区中。缓冲区可以包含任何类型的数据。

上面提到的着色器是由 GPU 处理的一组指令。WebGPU 着色器使用一种名为 WebGPU 着色器语言 (WGSL) 的低级类似 Rust 的语言编写。

您可以通过多种不同的方式设计 WebGPU 应用程序,但该过程可能包含以下步骤:

  1. 创建着色器模块:使用 WGSL 编写着色器代码并将其打包到一个或多个着色器模块中。
  2. 获取并配置画布上下文:获取 <canvas> 元素的 webgpu 上下文,并将其配置为接收来自 GPU 逻辑设备的有关要渲染的图形的信息。如果您的应用程序没有图形输出,例如仅使用计算管道的应用程序,则不需要此步骤。
  3. 创建包含您的数据的资源:您希望由管道处理的数据需要存储在 GPU 缓冲区或纹理中,以便您的应用程序访问。
  4. 创建管道:定义管道描述符,详细描述所需的管道,包括所需的数据结构、绑定、着色器和资源布局,然后从中创建管道。我们的基本演示只包含一个管道,但重要的应用程序通常会包含多个管道,用于不同的目的。
  5. 运行计算/渲染传递:这包含许多子步骤
    1. 创建一个命令编码器,它可以编码一组命令,传递给 GPU 执行。
    2. 在计算/渲染命令发出时,创建一个传递编码器对象。
    3. 运行命令来指定使用哪个管道,从哪个缓冲区获取所需数据,运行多少个绘制操作(在渲染管道的情况下)等等。
    4. 完成命令列表,并将其封装在命令缓冲区中。
    5. 通过逻辑设备的命令队列将命令缓冲区提交给 GPU。

在下面的部分中,我们将检查一个基本的渲染管道演示,以便您可以探索它需要什么。稍后,我们还将检查一个基本的计算管道示例,了解它与渲染管道有何不同。

基本渲染管道

在我们的基本渲染演示中,我们为一个<canvas>元素设置了一个纯蓝色的背景,并在其上绘制了一个三角形。

创建着色器模块

我们使用以下着色器代码。顶点着色器阶段(@vertex块)接受包含位置和颜色的数据块,根据给定的位置定位顶点,插值颜色,然后将数据传递给片段着色器阶段。片段着色器阶段(@fragment块)接受来自顶点着色器阶段的数据,并根据给定的颜色为顶点着色。

js
const shaders = `
struct VertexOut {
  @builtin(position) position : vec4f,
  @location(0) color : vec4f
}

@vertex
fn vertex_main(@location(0) position: vec4f,
               @location(1) color: vec4f) -> VertexOut
{
  var output : VertexOut;
  output.position = position;
  output.color = color;
  return output;
}

@fragment
fn fragment_main(fragData: VertexOut) -> @location(0) vec4f
{
  return fragData.color;
}
`;

注意:在我们的演示中,我们正在模板字面量内存储着色器代码,但您可以将其存储在任何可以轻松检索为文本以馈送到 WebGPU 程序的地方。例如,另一种常见做法是将着色器存储在<script>元素中,并使用Node.textContent检索其内容。用于 WGSL 的正确 MIME 类型是 text/wgsl

要使您的着色器代码可供 WebGPU 使用,您必须通过GPUDevice.createShaderModule()调用将其放入GPUShaderModule中,并将您的着色器代码作为描述符对象内的属性传递。例如

js
const shaderModule = device.createShaderModule({
  code: shaders,
});

获取并配置画布上下文

在渲染管道中,我们需要指定某个位置来渲染图形。在本例中,我们获取对屏幕上的<canvas>元素的引用,然后使用参数webgpu调用HTMLCanvasElement.getContext()来返回其 GPU 上下文(一个GPUCanvasContext实例)。

从那里,我们通过调用GPUCanvasContext.configure()来配置上下文,并将包含渲染信息来源的GPUDevice、纹理将具有的格式以及渲染半透明纹理时使用的 alpha 模式传递给它。

js
const canvas = document.querySelector("#gpuCanvas");
const context = canvas.getContext("webgpu");

context.configure({
  device: device,
  format: navigator.gpu.getPreferredCanvasFormat(),
  alphaMode: "premultiplied",
});

注意:确定纹理格式的最佳实践是使用GPU.getPreferredCanvasFormat()方法;这将为用户的设备选择最有效的格式(bgra8unormrgba8unorm)。

创建缓冲区并将我们的三角形数据写入其中

接下来,我们将以 WebGPU 程序可以使用的形式提供我们的数据。我们的数据最初提供在Float32Array中,它包含每个三角形顶点的 8 个数据点——X、Y、Z、W 用于位置,以及 R、G、B、A 用于颜色。

js
const vertices = new Float32Array([
  0.0, 0.6, 0, 1, 1, 0, 0, 1, -0.5, -0.6, 0, 1, 0, 1, 0, 1, 0.5, -0.6, 0, 1, 0,
  0, 1, 1,
]);

但是,这里我们遇到了一个问题。我们需要将我们的数据放入GPUBuffer中。在幕后,这种类型的缓冲区存储在与 GPU 内核紧密集成的内存中,以实现所需的高性能处理。作为副作用,此内存无法被主机系统(如浏览器)上运行的进程访问。

通过调用GPUDevice.createBuffer()创建GPUBuffer。我们为其提供等于vertices数组长度的大小,以便它可以包含所有数据,以及VERTEXCOPY_DST使用标志,以指示缓冲区将用作顶点缓冲区和复制操作的目标。

js
const vertexBuffer = device.createBuffer({
  size: vertices.byteLength, // make it big enough to store vertices in
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});

我们可以使用映射操作来处理将数据放入GPUBuffer中,就像我们在计算管道示例中用于从 GPU 读取数据返回到 JavaScript 一样。但是,在本例中,我们将使用方便的GPUQueue.writeBuffer()便捷方法,它将要写入的缓冲区、要从中写入的数据源、每个缓冲区的偏移量值以及要写入的数据大小(我们指定了数组的整个长度)作为其参数。然后浏览器会找出处理数据写入的最有效方法。

js
device.queue.writeBuffer(vertexBuffer, 0, vertices, 0, vertices.length);

定义和创建渲染管道

现在我们已将数据放入缓冲区中,设置的下一部分是实际创建我们的管道,准备用于渲染。

首先,我们创建一个对象,该对象描述了顶点数据所需的布局。这完美地描述了我们之前在vertices数组和顶点着色器阶段中看到的内容——每个顶点都具有位置和颜色数据。两者都以float32x4格式(映射到 WGSL vec4<f32>类型)格式化,颜色数据从每个顶点的 16 字节偏移量开始。arrayStride指定步长,即构成每个顶点的字节数,stepMode指定应逐顶点获取数据。

js
const vertexBuffers = [
  {
    attributes: [
      {
        shaderLocation: 0, // position
        offset: 0,
        format: "float32x4",
      },
      {
        shaderLocation: 1, // color
        offset: 16,
        format: "float32x4",
      },
    ],
    arrayStride: 32,
    stepMode: "vertex",
  },
];

接下来,我们创建一个描述符对象,该对象指定渲染管道阶段的配置。对于两个着色器阶段,我们都指定了可以找到相关代码的GPUShaderModuleshaderModule)以及用作每个阶段入口点的函数的名称。

此外,在顶点着色器阶段的情况下,我们提供了vertexBuffers对象以提供顶点数据的预期状态。在我们的片段着色器阶段的情况下,我们提供了一个颜色目标状态数组,指示指定的渲染格式(这与我们之前在画布上下文配置中指定的格式匹配)。

我们还指定了一个primitive状态,在本例中仅声明我们将绘制的图元类型,以及autolayoutlayout属性定义了在管道执行期间使用的所有 GPU 资源(缓冲区、纹理等)的布局(结构、用途和类型)。在更复杂的应用程序中,这将采用GPUPipelineLayout对象的格式,使用GPUDevice.createPipelineLayout()创建(您可以在我们的基本计算管道中看到一个示例),这允许 GPU 提前找出如何最有效地运行管道。但是,在这里我们指定了auto值,这将导致管道根据着色器代码中定义的任何绑定生成隐式绑定组布局。

js
const pipelineDescriptor = {
  vertex: {
    module: shaderModule,
    entryPoint: "vertex_main",
    buffers: vertexBuffers,
  },
  fragment: {
    module: shaderModule,
    entryPoint: "fragment_main",
    targets: [
      {
        format: navigator.gpu.getPreferredCanvasFormat(),
      },
    ],
  },
  primitive: {
    topology: "triangle-list",
  },
  layout: "auto",
};

最后,我们可以基于pipelineDescriptor对象创建一个GPURenderPipeline,方法是将其作为参数传递给GPUDevice.createRenderPipeline()方法调用。

js
const renderPipeline = device.createRenderPipeline(pipelineDescriptor);

运行渲染传递

现在所有设置都已完成,我们实际上可以运行渲染传递并将某些内容绘制到我们的<canvas>上。要编码稍后要发出到 GPU 的任何命令,您需要创建一个GPUCommandEncoder实例,这可以通过GPUDevice.createCommandEncoder()调用来完成。

js
const commandEncoder = device.createCommandEncoder();

接下来,我们通过使用GPUCommandEncoder.beginRenderPass()调用创建GPURenderPassEncoder实例来启动渲染传递运行。此方法将描述符对象作为参数,其唯一必需的属性是colorAttachments数组。在本例中,我们指定

  1. 要渲染到的纹理视图;我们通过context.getCurrentTexture().createView()<canvas>创建了一个新视图。
  2. 加载并开始绘制之前,该视图应清除为指定颜色。这就是导致三角形后面出现蓝色背景的原因。
  3. 此颜色附件应存储当前渲染传递的值。
js
const clearColor = { r: 0.0, g: 0.5, b: 1.0, a: 1.0 };

const renderPassDescriptor = {
  colorAttachments: [
    {
      clearValue: clearColor,
      loadOp: "clear",
      storeOp: "store",
      view: context.getCurrentTexture().createView(),
    },
  ],
};

const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);

现在我们可以调用渲染传递编码器的方法来绘制我们的三角形

  1. GPURenderPassEncoder.setPipeline()使用我们的renderPipeline对象作为参数调用,以指定渲染传递要使用的管道。
  2. GPURenderPassEncoder.setVertexBuffer()使用我们的vertexBuffer对象作为参数调用,用作要传递到管道的渲染数据源。第一个参数是要设置顶点缓冲区的插槽,它是描述此缓冲区布局的vertexBuffers数组中元素索引的引用。
  3. GPURenderPassEncoder.draw()启动绘制。我们的vertexBuffer中包含三个顶点的数据,因此我们设置了3的顶点计数值来绘制所有顶点。
js
passEncoder.setPipeline(renderPipeline);
passEncoder.setVertexBuffer(0, vertexBuffer);
passEncoder.draw(3);

要完成编码命令序列并将其发出到 GPU,还需要三个步骤。

  1. 我们调用GPURenderPassEncoder.end()方法来表示渲染传递命令列表的结束。
  2. 我们调用GPUCommandEncoder.finish()方法来完成已发出命令序列的记录,并将其封装到GPUCommandBuffer对象实例中。
  3. 我们将GPUCommandBuffer提交到设备的命令队列(由GPUQueue实例表示),以发送到 GPU。设备的队列可以通过GPUDevice.queue属性获得,并且可以通过GPUQueue.submit()调用将GPUCommandBuffer实例数组添加到队列中。

可以通过以下两行代码实现这三个步骤

js
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

基本计算管道

在我们的基本计算演示中,我们让 GPU 计算一些值,将它们存储在输出缓冲区中,将数据复制到暂存缓冲区,然后映射该暂存缓冲区,以便可以将数据读出到 JavaScript 并记录到控制台中。

该应用程序遵循与基本渲染演示类似的结构。我们像以前一样创建GPUDevice引用,并将我们的着色器代码封装到GPUShaderModule中,方法是通过GPUDevice.createShaderModule()调用。这里不同的是,我们的着色器代码只有一个着色器阶段,一个@compute阶段

js
// Define global buffer size
const BUFFER_SIZE = 1000;

const shader = `
@group(0) @binding(0)
var<storage, read_write> output: array<f32>;

@compute @workgroup_size(64)
fn main(
  @builtin(global_invocation_id)
  global_id : vec3u,

  @builtin(local_invocation_id)
  local_id : vec3u,
) {
  // Avoid accessing the buffer out of bounds
  if (global_id.x >= ${BUFFER_SIZE}) {
    return;
  }

  output[global_id.x] =
    f32(global_id.x) * 1000. + f32(local_id.x);
}
`;

创建缓冲区来处理我们的数据

在此示例中,我们创建了两个GPUBuffer实例来处理我们的数据,一个output缓冲区以高速写入 GPU 计算结果,以及一个stagingBuffer,我们将复制output内容到其中,该内容可以映射以允许 JavaScript 访问这些值。

  • output被指定为存储缓冲区,它将是复制操作的源。
  • stagingBuffer被指定为可以由 JavaScript 映射以供读取的缓冲区,并且将是复制操作的目标。
js
const output = device.createBuffer({
  size: BUFFER_SIZE,
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});

const stagingBuffer = device.createBuffer({
  size: BUFFER_SIZE,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});

创建绑定组布局

在创建管道时,我们指定一个要用于管道的绑定组。这涉及首先创建一个GPUBindGroupLayout(通过调用GPUDevice.createBindGroupLayout()),它定义了GPU资源(例如将在该管道中使用的缓冲区)的结构和用途。此布局用作绑定组必须遵守的模板。在本例中,我们向管道提供对单个内存缓冲区的访问权限,该缓冲区绑定到绑定槽 0(这与我们着色器代码中的相关绑定编号匹配 - @binding(0)),可在管道的计算阶段使用,并且缓冲区的用途定义为storage

js
const bindGroupLayout = device.createBindGroupLayout({
  entries: [
    {
      binding: 0,
      visibility: GPUShaderStage.COMPUTE,
      buffer: {
        type: "storage",
      },
    },
  ],
});

接下来,我们通过调用GPUDevice.createBindGroup()来创建一个GPUBindGroup。我们将此方法调用传递一个描述符对象,该对象指定要以此为基础创建绑定组的绑定组布局,以及要绑定到布局中定义的槽的变量的详细信息。在本例中,我们正在声明绑定 0,并指定我们之前定义的output缓冲区应绑定到它。

js
const bindGroup = device.createBindGroup({
  layout: bindGroupLayout,
  entries: [
    {
      binding: 0,
      resource: {
        buffer: output,
      },
    },
  ],
});

注意:可以通过调用GPUComputePipeline.getBindGroupLayout()方法来检索一个在创建绑定组时使用的隐式布局。渲染管道也提供了一个版本:请参阅GPURenderPipeline.getBindGroupLayout()

创建计算管道

在上述所有内容就绪后,我们现在可以通过调用GPUDevice.createComputePipeline()来创建一个计算管道,并将其传递一个管道描述符对象。这与创建渲染管道的操作方式类似。我们描述计算着色器,指定在哪个模块中查找代码以及入口点是什么。我们还为管道指定一个layout,在本例中,我们通过GPUDevice.createPipelineLayout()调用基于我们之前定义的bindGroupLayout创建一个布局。

js
const computePipeline = device.createComputePipeline({
  layout: device.createPipelineLayout({
    bindGroupLayouts: [bindGroupLayout],
  }),
  compute: {
    module: shaderModule,
    entryPoint: "main",
  },
});

这里与渲染管道布局的一个区别是我们没有指定基本类型,因为我们没有绘制任何内容。

运行计算传递

运行计算传递的结构类似于运行渲染传递,但有一些不同的命令。首先,传递编码器使用GPUCommandEncoder.beginComputePass()创建。

在发出命令时,我们像以前一样指定要使用的管道,使用GPUComputePassEncoder.setPipeline()。但是,然后我们使用GPUComputePassEncoder.setBindGroup()指定我们想要使用我们的bindGroup来指定要在计算中使用的数据,并使用GPUComputePassEncoder.dispatchWorkgroups()指定要用于运行计算的GPU工作组的数量。

然后,我们使用GPURenderPassEncoder.end()发出渲染传递命令列表结束的信号。

js
passEncoder.setPipeline(computePipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatchWorkgroups(Math.ceil(BUFFER_SIZE / 64));

passEncoder.end();

将结果读回JavaScript

在使用GPUQueue.submit()将编码的命令提交到GPU以执行之前,我们使用GPUCommandEncoder.copyBufferToBuffer()output缓冲区的内容复制到stagingBuffer缓冲区。

js
// Copy output buffer to staging buffer
commandEncoder.copyBufferToBuffer(
  output,
  0, // Source offset
  stagingBuffer,
  0, // Destination offset
  BUFFER_SIZE,
);

// End frame by passing array of command buffers to command queue for execution
device.queue.submit([commandEncoder.finish()]);

一旦输出数据在stagingBuffer中可用,我们就使用GPUBuffer.mapAsync()方法将数据映射到中间内存,使用GPUBuffer.getMappedRange()获取映射范围的引用,将数据复制到JavaScript,然后将其记录到控制台。我们还在完成使用后取消映射stagingBuffer

js
// map staging buffer to read results back to JS
await stagingBuffer.mapAsync(
  GPUMapMode.READ,
  0, // Offset
  BUFFER_SIZE, // Length
);

const copyArrayBuffer = stagingBuffer.getMappedRange(0, BUFFER_SIZE);
const data = copyArrayBuffer.slice();
stagingBuffer.unmap();
console.log(new Float32Array(data));

GPU 错误处理

WebGPU 调用在 GPU 进程中异步验证。如果发现错误,则问题调用将在 GPU 端标记为无效。如果发出另一个依赖于无效调用的返回值的调用,则该对象也将被标记为无效,依此类推。因此,WebGPU 中的错误被称为“传染性”。

每个GPUDevice实例都维护自己的错误范围栈。此栈最初为空,但您可以通过调用GPUDevice.pushErrorScope()将错误范围推送到栈中以捕获特定类型的错误。

完成错误捕获后,您可以通过调用GPUDevice.popErrorScope()结束捕获。这会将范围从栈中弹出并返回一个Promise,该Promise 解析为一个对象(GPUInternalErrorGPUOutOfMemoryErrorGPUValidationError),描述在范围内捕获的第一个错误,或者如果未捕获任何错误则返回null

我们已尝试提供有用的信息,以帮助您了解为什么在您的 WebGPU 代码中出现错误,并在适当的地方提供“验证”部分,其中列出了避免错误的标准。例如,请参阅GPUDevice.createBindGroup() 验证部分。其中一些信息很复杂;我们决定不重复规范,而是仅列出错误标准,这些标准是

  • 不明显的,例如导致验证错误的描述符属性组合。没有必要告诉您确保使用正确的描述符对象结构。这既显而易见又含糊不清。
  • 开发人员控制的。一些错误标准纯粹基于内部机制,与 Web 开发人员无关。

您可以在解释器中找到有关 WebGPU 错误处理的更多信息 - 请参阅对象有效性和已销毁状态错误WebGPU 错误处理最佳实践提供了有用的现实世界示例和建议。

注意:处理 WebGL 中错误的传统方法是提供一个getError()方法来返回错误信息。这存在问题,因为它同步返回错误,这对性能不利 - 每次调用都需要往返 GPU,并且需要所有先前发出的操作都完成。它的状态模型也是扁平的,这意味着错误可能会在不相关的代码之间泄漏。WebGPU 的创建者决心改进这一点。

接口

API 的入口点

API 的入口点 - 返回当前上下文的GPU对象。

GPU

使用 WebGPU 的起点。它可用于返回GPUAdapter

GPUAdapter

表示 GPU 适配器。从中您可以请求GPUDevice、适配器信息、功能和限制。

GPUAdapterInfo

包含有关适配器的识别信息。

配置 GPU 设备

GPUDevice

表示逻辑 GPU 设备。这是访问大多数 WebGPU 功能的主要接口。

GPUSupportedFeatures

一个类似 Set 的对象,描述GPUAdapterGPUDevice支持的其他功能。

GPUSupportedLimits

描述GPUAdapterGPUDevice支持的限制。

配置渲染<canvas>

HTMLCanvasElement.getContext() - "webgpu" contextType

使用"webgpu" contextType调用getContext()会返回一个GPUCanvasContext对象实例,然后可以使用GPUCanvasContext.configure()对其进行配置。

GPUCanvasContext

表示<canvas>元素的 WebGPU 渲染上下文。

表示管道资源

GPUBuffer

表示可以用于存储原始数据以在 GPU 操作中使用的内存块。

GPUExternalTexture

一个包装器对象,包含一个HTMLVideoElement快照,该快照可用作 GPU 渲染操作中的纹理。

GPUSampler

控制着色器如何转换和过滤纹理资源数据。

GPUShaderModule

对内部着色器模块对象的引用,它是 WGSL 着色器代码的容器,可以提交到 GPU 以由管道执行。

GPUTexture

用于存储 1D、2D 或 3D 数据数组(例如图像)以在 GPU 渲染操作中使用的容器。

GPUTextureView

对特定GPUTexture定义的纹理子资源的某个子集的视图。

表示管道

GPUBindGroup

基于GPUBindGroupLayoutGPUBindGroup定义了一组要一起绑定到组中的资源以及如何在着色器阶段使用这些资源。

GPUBindGroupLayout

定义了将在管道中使用的相关 GPU 资源(如缓冲区)的结构和用途,并在创建GPUBindGroup时用作模板。

GPUComputePipeline

控制计算着色器阶段,可以在GPUComputePassEncoder中使用。

GPUPipelineLayout

定义了管道使用的GPUBindGroupLayout。在命令编码期间与管道一起使用的GPUBindGroup必须具有兼容的GPUBindGroupLayout

GPURenderPipeline

控制顶点和片段着色器阶段,可以在GPURenderPassEncoderGPURenderBundleEncoder中使用。

编码并将命令提交到 GPU

GPUCommandBuffer

表示可以提交到GPUQueue以执行的已记录的 GPU 命令列表。

GPUCommandEncoder

表示命令编码器,用于编码要发出到 GPU 的命令。

GPUComputePassEncoder

编码与控制计算着色器阶段相关的命令,如GPUComputePipeline发出的命令。是GPUCommandEncoder整体编码活动的一部分。

GPUQueue

控制 GPU 上编码命令的执行。

GPURenderBundle

预记录的命令捆绑包的容器(请参阅GPURenderBundleEncoder)。

GPURenderBundleEncoder

用于预记录命令捆绑包。这些可以在GPURenderPassEncoder中通过executeBundles()方法重复使用,次数不限。

GPURenderPassEncoder

编码与控制顶点和片段着色器阶段相关的命令,如GPURenderPipeline发出的命令。是GPUCommandEncoder整体编码活动的一部分。

在渲染传递上运行查询

GPUQuerySet

用于记录传递上查询的结果,例如遮挡或时间戳查询。

调试错误

GPUCompilationInfo

一个GPUCompilationMessage对象的数组,由 GPU 着色器模块编译器生成,以帮助诊断着色器代码的问题。

GPUCompilationMessage

表示 GPU 着色器模块编译器生成的单个信息、警告或错误消息。

GPUDeviceLostInfo

GPUDevice.lostPromise解析时返回,提供有关设备丢失原因的信息。

GPUError

GPUDevice.popErrorScopeuncapturederror事件显示的错误的基本接口。

GPUInternalError

GPUDevice.popErrorScopeGPUDeviceuncapturederror事件显示的错误类型之一。表示操作由于系统或实现特定原因而失败,即使满足所有验证要求。

GPUOutOfMemoryError

GPUDevice.popErrorScopeGPUDeviceuncapturederror 事件引发的错误类型之一。表示没有足够的可用内存来完成请求的操作。

GPUPipelineError

描述管道故障。当 Promise(由 GPUDevice.createComputePipelineAsync()GPUDevice.createRenderPipelineAsync() 调用返回)被拒绝时接收到的值。

GPUUncapturedErrorEvent

GPUDevice uncapturederror 事件的事件对象类型。

GPUValidationError

GPUDevice.popErrorScopeGPUDeviceuncapturederror 事件引发的错误类型之一。描述应用程序错误,表示操作未通过 WebGPU API 的验证约束。

安全要求

整个 API 仅在 安全上下文 中可用。

示例

规范

规范
WebGPU
# gpu-interface

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅