# WebGPU API

> navigator.gpu.requestAdapter() 与 requestDevice() 把 GPU 交给页面。适配器与设备选项、每种拒绝、设备丢失处理与 WebGL2 回退。

WebGPU 通过 `navigator.gpu` 把系统 GPU 交给页面做渲染和通用计算：`requestAdapter()` 选出一块物理或软件适配器，`requestDevice()` 把它变成用来创建缓冲区、纹理、管线和命令编码器的 `GPUDevice`，着色器用 WGSL 而不是 WebGL 的 GLSL 编写。整个 API 带 `[SecureContext]`，并同时暴露在 `WorkerNavigator` 上，所以专用 Worker 可以独自持有设备。

支持情况按操作系统而非仅按浏览器版本划分（BCD `api.GPU`）。Chrome 113 在 ChromeOS、macOS 和 Windows 上发布；Chrome 144 为 Intel Gen12 及更新的 GPU 加入 Linux；Chrome 121 覆盖 Android。Firefox 141 只在 Windows 上发布，Apple 芯片 macOS 随后在 Firefox 145（macOS Tahoe）和 147（更早的 macOS）加入，不支持 Intel Mac（[bug 2004105](https://bugzil.la/2004105)）、不支持 Linux（[bug 2006676](https://bugzil.la/2006676)）、不支持 Service Worker 上下文（[bug 1942431](https://bugzil.la/1942431)）；Android 版 Firefox 没有实现。Safari 26 在 macOS、iOS、iPadOS 和 visionOS 上发布。

## 语法

```js
navigator.gpu.requestAdapter()
navigator.gpu.requestAdapter(options)

adapter.requestDevice()
adapter.requestDevice(descriptor)

navigator.gpu.getPreferredCanvasFormat()
canvas.getContext("webgpu")
```

`requestAdapter()` 返回 `Promise<GPUAdapter?>`：浏览器没有满足 `options` 的适配器时以 `null` 兑现而不是拒绝。`requestDevice()` 返回 `Promise<GPUDevice>` 并消耗掉适配器；对同一个 `GPUAdapter` 再调用一次会被拒绝。`getPreferredCanvasFormat()` 返回当前系统上 canvas 上下文应配置的 `GPUTextureFormat`（`"bgra8unorm"` 或 `"rgba8unorm"`）。

## 参数

`requestAdapter()` 接受可选的 `GPURequestAdapterOptions` 字典；`requestDevice()` 接受可选的 `GPUDeviceDescriptor`。两者每个成员都有默认值，不带参数的调用是合法的。

| 成员 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `options.featureLevel` | `DOMString` | 否 | `"core"`（默认）或 `"compatibility"`。请求至少支持该级别限制与特性的适配器；不支持的级别以 `null` 兑现。未实现兼容模式的浏览器按 `"core"` 处理。 |
| `options.powerPreference` | `GPUPowerPreference` | 否 | `"low-power"` 或 `"high-performance"`，仅为提示。Windows 上的 Chrome 忽略它，复用浏览器已经分配的那块适配器（[crbug.com/369219127](https://crbug.com/369219127)）。 |
| `options.forceFallbackAdapter` | `boolean` | 否 | 默认 `false`。为 `true` 时只允许返回软件（回退）适配器；没有则为 `null`。 |
| `options.xrCompatible` | `boolean` | 否 | 默认 `false`。请求能向 WebXR 会话呈现的适配器。 |
| `descriptor.requiredFeatures` | `sequence<GPUFeatureName>` | 否 | 默认 `[]`。设备必须暴露的特性名；设备上启用的恰好就是这个集合，所以漏列的可选特性即使适配器有也不可用。 |
| `descriptor.requiredLimits` | `record<DOMString, GPUSize64 or undefined>` | 否 | 默认 `{}`。设备必须满足的限制；每个键必须是受支持的限制名，值不得超过适配器自身的值。设备上的校验按这些确切限制进行，而不是按适配器的。 |
| `descriptor.defaultQueue` | `GPUQueueDescriptor` | 否 | 默认 `{}`。只带 `device.queue` 的 `label`。 |
| `descriptor.label` | `USVString` | 否 | 继承自 `GPUObjectDescriptorBase`；出现在错误消息和 DevTools 中。 |

## 异常

`requestAdapter()` 没有因缺少 GPU 而拒绝的路径：没有适配器或适配器不合适都以 `null` 兑现。`requestDevice()` 以两种名称拒绝，顺序即规范的检查顺序（[GPUAdapter.requestDevice()](https://www.w3.org/TR/webgpu/#dom-gpuadapter-requestdevice)（w3.org））。

| 异常 | 条件 |
|---|---|
| `TypeError` | `requiredFeatures` 不是 `adapter.features` 的子集。规范有意让「浏览器根本不认识这个名字」和「这块适配器没有它」报同一个错误。 |
| `OperationError` | 适配器已被更早的 `requestDevice()` 消耗；或 `requiredLimits` 的某个键不是受支持的限制；或某个值优于适配器的限制；或对齐类限制不是小于 2^32 的 2 的幂。 |

有两种失败不是拒绝。适配器已过期或浏览器无法满足请求时，`requestDevice()` 仍然兑现，只是给出的设备已经丢失：`device.lost` 在 `requestDevice()` 的 Promise 兑现之前就以 `reason` 为 `"unknown"` 的 `GPUDeviceLostInfo` 兑现。使用过程中的校验错误、显存不足和内部错误以 `GPUValidationError`、`GPUOutOfMemoryError`、`GPUInternalError` 对象的形式经 `device.popErrorScope()` 或 `uncapturederror` 事件到达，从不以抛出异常的方式出现。

:::observed
在 Chrome 的 `WebGPUService` 特性被禁用的平台上，第一次调用 `navigator.gpu.requestAdapter()` 会在兑现前打印一条 info 级别的控制台消息：`WebGPU is experimental on this platform. See https://github.com/gpuweb/gpuweb/wiki/Implementation-Status#implementation-status`。传入适配器没有的特性，例如在没有 ASTC 的桌面适配器上调用 `requestDevice({ requiredFeatures: ["texture-compression-astc"] })`，会以 `TypeError: Unsupported feature: texture-compression-astc` 拒绝。两条字符串分别位于 Chromium 的 [`gpu.cc`](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/webgpu/gpu.cc) 与 [`gpu_adapter.cc`](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc)（chromium.googlesource.com）。
:::

## 示例

每个示例都先检查 `navigator.gpu`，再处理 `null` 适配器，因为两种情况在已发布的浏览器上都会出现：Android 版 Firefox 没有 `navigator.gpu`，Intel Mac 上的 Firefox 有 `navigator.gpu` 却拿不到适配器。

### 检测支持并回退到 WebGL2

特性检测分两步：属性存在，以及确实返回了适配器。两种失败同样处理，把 canvas 交给 WebGL2 渲染器，Chrome 56、Firefox 51 和 Safari 15 都提供 WebGL2。

```js
async function createRenderer(canvas) {
  if (!("gpu" in navigator)) {
    return createWebGL2Renderer(canvas);
  }
  const adapter = await navigator.gpu.requestAdapter();
  if (!adapter) {
    return createWebGL2Renderer(canvas);
  }
  const device = await adapter.requestDevice();
  const context = canvas.getContext("webgpu");
  context.configure({ device, format: navigator.gpu.getPreferredCanvasFormat() });
  return createWebGPURenderer(device, context);
}

function createWebGL2Renderer(canvas) {
  const gl = canvas.getContext("webgl2");
  if (!gl) throw new Error("WebGPU 与 WebGL2 都不可用");
  return { kind: "webgl2", gl };
}
```

对已经持有 2D 或 WebGL 上下文的 canvas 调用 `getContext("webgpu")` 返回 `null`，所以要在任何回退代码碰到同一个元素之前先创建 WebGPU 上下文。

### 请求可选特性与限制而不让设备请求失败

先问适配器有什么，再只请求它给得了的。列出缺失的特性会以 `TypeError` 拒绝；请求高于适配器的限制会以 `OperationError` 拒绝，所以要按 `adapter.limits` 收紧而不是写死数值。

```js
async function requestCapableDevice(adapter) {
  const requiredFeatures = [];
  for (const name of ["timestamp-query", "texture-compression-bc", "shader-f16"]) {
    if (adapter.features.has(name)) requiredFeatures.push(name);
  }
  const wanted = 1024 * 1024 * 1024; // 1 GiB 存储缓冲区
  const requiredLimits = {
    maxStorageBufferBindingSize: Math.min(wanted, adapter.limits.maxStorageBufferBindingSize),
  };
  try {
    return await adapter.requestDevice({ requiredFeatures, requiredLimits });
  } catch (err) {
    if (err.name === "OperationError") {
      return adapter.requestDevice(); // 被拒绝后适配器仍未被消耗
    }
    throw err;
  }
}
```

被拒绝的 `requestDevice()` 不会消耗适配器，所以用默认参数重试可以在同一个 `GPUAdapter` 上进行。

### 从设备丢失中恢复

`device.lost` 是每个设备只会落定一次的 Promise：你自己调用 `device.destroy()` 后 `reason` 为 `"destroyed"`，其他情况（驱动重置、GPU 进程崩溃、内存紧张的手机把标签页切到后台）为 `"unknown"`。由丢失设备创建的每个 `GPUBuffer`、`GPUTexture` 和管线都已失效，恢复要从 `requestAdapter()` 重新开始。

```js
async function initGpu(canvas, onLost) {
  const adapter = await navigator.gpu.requestAdapter();
  if (!adapter) return null;
  const device = await adapter.requestDevice();

  device.lost.then((info) => {
    console.warn(`GPU 设备丢失（${info.reason}）：${info.message}`);
    if (info.reason !== "destroyed") onLost(); // 重新执行 initGpu 并重建资源
  });

  device.addEventListener("uncapturederror", (event) => {
    console.error(event.error.message);
  });
  return device;
}
```

`uncapturederror` 监听器接住没有被 `pushErrorScope()`/`popErrorScope()` 包住的校验失败；没有它时 Chrome 只在 DevTools 控制台里报告这些错误。

## 另请参阅

- [WebCodecs](/zh/reference/capabilities/webcodecs/)，其 `VideoFrame` 对象可作为 WebGPU 外部纹理导入
- [PWA 的 Core Web Vitals](/zh/reference/performance/core-web-vitals/)，主线程上的 GPU 工作会体现为 INP
- [离线策略](/zh/guides/offline/)，缓存 WebGPU 应用下载的 WGSL 与模型资源
- [WebGPU: GPU.requestAdapter()](https://www.w3.org/TR/webgpu/#dom-gpu-requestadapter)（w3.org）
- [WebGPU: GPUAdapter.requestDevice()](https://www.w3.org/TR/webgpu/#dom-gpuadapter-requestdevice)（w3.org）
- [WebGPU implementation status](https://github.com/gpuweb/gpuweb/wiki/Implementation-Status)（github.com）
- [Chrome ships WebGPU](https://developer.chrome.com/blog/webgpu-release)（developer.chrome.com）
- [News from WWDC25: web technology coming this fall in Safari 26 beta](https://webkit.org/blog/16993/news-from-wwdc25-web-technology-coming-this-fall-in-safari-26-beta/)（webkit.org）