WebGPU:浏览器 GPU 编程的新纪元

Author Avatar
via
发表:2026-07-15 09:01:10
修改:2026-07-15 09:01:08

引言

WebGPU 是浏览器图形 API 的下一代标准,旨在取代服役多年的 WebGL。经过 W3C "GPU for the Web" 工作组数年的打磨,WebGPU 已在 Chrome、Edge 等主流浏览器中正式落地,Safari 也在持续跟进。它不仅仅是一次图形渲染的升级——尽管渲染是大多数人对它的第一印象——更是一次将 GPU 通用计算能力引入 Web 平台的根本性变革。

本文将从架构设计、核心概念、与 WebGL 的对比、计算着色器实战等角度,全面拆解 WebGPU,帮助你理解它为什么被如此看重,以及如何在项目中开始实践。

一、为什么需要 WebGPU?

WebGL 诞生于 2011 年,它是 OpenGL ES 2.0 的浏览器封装。在那个年代,它确实把硬件加速的 3D 图形带到了 Web,催生了 Three.js 等生态。但 WebGL 本质上是对固定管线的封装,它有几个根本性的局限:

  • 无计算着色器支持:WebGL 1/2 不支持 GPGPU 通用计算,只能用 hack 的方式把数据塞进纹理,再通过像素着色器间接计算,效率低且表达力弱。
  • 单线程状态机:所有 GPU 命令都在主线程上排队,CPU 侧的准备工作(编译着色器、上传缓冲)容易造成帧率抖动。
  • 全局状态管理:WebGL 大量依赖隐式全局状态(当前绑定的 buffer、texture、program),容易出 bug,也难以做命令录制。
  • 跨平台一致性差:WebGL 基于 GLSL,不同平台的 GLSL 编译器行为不一致,需要大量 polyfill。

WebGPU 从零开始设计,直接对接现代图形 API(Vulkan、Metal、D3D12)的理念,解决了上述所有问题。

二、WebGPU 核心架构

2.1 Adapter 与 Device

WebGPU 的入口是 navigator.gpu。通过 requestAdapter() 获取一个 GPU 适配器(对应物理显卡的一个逻辑实例),再通过 adapter.requestDevice() 获取一个 GPUDevice。Device 是你与 GPU 交互的主要上下文,所有资源(缓冲、纹理、管线)都从 Device 创建。

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

2.2 Pipeline:渲染管线与计算管线

WebGPU 引入了显式的管线对象(Pipeline)。你不再像 WebGL 那样分别编译 vertex 和 fragment shader 然后链接 program,而是一次性创建完整的 GPURenderPipelineGPUComputePipeline

管线对象是不可变的,创建时就完成着色器编译和管线状态绑定。这意味着你可以在非主线程编译着色器(通过 Web Worker),在合适的时机才提交到主线程使用,避免了运行时卡顿。

2.3 Bind Group:资源绑定的革新

WebGL 中绑定 uniform、texture 是一个个独立的调用,而且状态是全局的。WebGPU 引入了 Bind Group 概念,类似于 Vulkan 的 Descriptor Set:

const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [
    { binding: 0, resource: uniformBuffer },
    { binding: 1, resource: sampler },
    { binding: 2, resource: textureView },
  ],
});

一个 Bind Group 把一组资源打包在一起,渲染时只需 passEncoder.setBindGroup(0, bindGroup) 一行代码完成所有绑定。这不仅减少了 draw call 的开销,还让资源管理更清晰。

2.4 Command Encoder:命令录制

WebGPU 采用 命令录制 模型。你通过 GPUCommandEncoder 录制一系列 GPU 命令,然后一次性提交:

const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({ /* ... */ });
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.setVertexBuffer(0, vertexBuffer);
pass.draw(vertexCount);
pass.end();
device.queue.submit([encoder.finish()]);

这种模式让 CPU 端的命令构建和 GPU 端的执行解耦,录制可以在任何时间进行(甚至多线程),只在 submit 时才真正提交给 GPU 队列。

三、WGSL:全新的着色器语言

WebGPU 没有沿用 GLSL,而是定义了一套新语言 WGSL(WebGPU Shading Language)。这个决定曾引发争议,但有其合理性:

  • 类型安全:WGSL 有严格的类型系统,编译期捕获更多错误。
  • 跨平台一致:WGSL 由浏览器编译为目标平台的原生着色器(SPIR-V / MSL / DXIL),消除 GLSL 的跨平台分歧。
  • 语法直观:类似 Rust 和 TypeScript 的混合体,对现代开发者更友好。

一个简单的顶点着色器示例:

@vertex
fn vs_main(
  @location(0) position: vec3<f32>,
  @location(1) color: vec3<f32>,
) -> VertexOutput {
  var out: VertexOutput;
  out.position = vec4(position, 1.0);
  out.color = color;
  return out;
}

四、计算着色器:WebGPU 的杀手锏

如果说渲染是 WebGPU 的常规操作,那 计算着色器 就是它的杀手锏。GPGPU 不再需要 hack 纹理,WebGPU 原生支持 compute pass。

一个计算着色器的例子——矩阵乘法:

@group(0) @binding(0) var<storage, read> a: array<f32>;
@group(0) @binding(1) var<storage, read> b: array<f32>;
@group(0) @binding(2) var<storage, write> result: array<f32>;

@compute @workgroup_size(8, 8)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
  let row = gid.x;
  let col = gid.y;
  let N = 64u;
  var sum: f32 = 0.0;
  for (var k = 0u; k < N; k++) {
    sum += a[row * N + k] * b[k * N + col];
  }
  result[row * N + col] = sum;
}

计算着色器的调度通过 workgroup 组织,可以高效处理大规模并行数据。这意味着:

  • ML 推理:可以在浏览器中直接跑轻量神经网络推理,无需依赖 WASM 或后端服务。
  • 物理模拟:粒子系统、流体模拟可以在 GPU 上实时计算。
  • 图像处理:滤波、卷积、色调映射等操作直接在 GPU 上完成。
  • 数据加密/压缩:利用 GPU 并行性加速数据处理。

五、与 WebGL 的性能对比

在实际测试中,WebGPU 相比 WebGL 有显著的性能提升:

  • Draw Call 开销:由于 Bind Group 和命令录制的预构建,CPU 端的 per-draw 开销大幅降低。复杂场景下 draw call 数量可以提升 3-5 倍而不掉帧。
  • 着色器编译:WGSL 编译可以在 Worker 中进行,不会阻塞主线程渲染,首帧时间显著缩短。
  • 计算性能:对于 GPGPU 任务,原生计算着色器比 WebGL 纹理 hack 快 2-10 倍,且代码量减少一半以上。
  • 内存管理:WebGPU 提供了更细粒度的内存控制,可以通过 mapAsync 异步映射缓冲,避免同步等待。

六、实战起步:创建你的第一个 WebGPU 应用

下面是一个最小可运行的 WebGPU 渲染示例,画一个三角形:

// 1. 初始化
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format });

// 2. 顶点数据
const vertices = new Float32Array([
   0.0,  0.5, 0.0,  1, 0, 0,
  -0.5, -0.5, 0.0,  0, 1, 0,
   0.5, -0.5, 0.0,  0, 0, 1,
]);
const vertexBuffer = device.createBuffer({
  size: vertices.byteLength,
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(vertexBuffer, 0, vertices);

// 3. 着色器
const shaderModule = device.createShaderModule({
  code: `
    struct VOut {
      @builtin(position) position: vec4<f32>,
      @location(0) color: vec3<f32>,
    }
    @vertex fn vs_main(
      @location(0) pos: vec3<f32>,
      @location(1) col: vec3<f32>,
    ) -> VOut {
      var out: VOut;
      out.position = vec4(pos, 1.0);
      out.color = col;
      return out;
    }
    @fragment fn fs_main(@location(0) color: vec3<f32>) 
      -> @location(0) vec4<f32> {
      return vec4(color, 1.0);
    }
  `,
});

// 4. 管线
const pipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex: {
    module: shaderModule,
    entryPoint: 'vs_main',
    buffers: [{
      arrayStride: 24,
      attributes: [
        { shaderLocation: 0, offset: 0, format: 'float32x3' },
        { shaderLocation: 1, offset: 12, format: 'float32x3' },
      ],
    }],
  },
  fragment: {
    module: shaderModule,
    entryPoint: 'fs_main',
    targets: [{ format }],
  },
});

// 5. 渲染
function render() {
  const encoder = device.createCommandEncoder();
  const pass = encoder.beginRenderPass({
    colorAttachments: [{
      view: context.getCurrentTexture().createView(),
      clearValue: { r: 0, g: 0, b: 0, a: 1 },
      loadOp: 'clear',
      storeOp: 'store',
    }],
  });
  pass.setPipeline(pipeline);
  pass.setVertexBuffer(0, vertexBuffer);
  pass.draw(3);
  pass.end();
  device.queue.submit([encoder.finish()]);
  requestAnimationFrame(render);
}
render();

虽然代码量看起来比 WebGL 三角形多,但这个例子已经包含了完整的管线创建、顶点布局、着色器编译等过程。在实际项目中,这些初始化只写一次,后续的复杂场景中 WebGPU 的代码组织优势会越来越明显。

七、生态与工具链

WebGPU 的生态正在快速成长:

  • Three.js:已内置 WebGPURenderer,支持 TSL(Three Shading Language),在 WebGPU 后端下可使用计算着色器节点。
  • Babylon.js:从 6.0 开始全面支持 WebGPU,可作为 WebGL 的直接替代后端。
  • WebGPU Insight:类似 Spectre 的调试工具,可查看管线状态、资源使用和帧分析。
  • Dawn / wgpu:原生级别的 WebGPU 实现,可用于 Electron、Flutter 等非浏览器环境。

八、浏览器兼容性与未来展望

截至 2026 年中期,WebGPU 在 Chrome 113+、Edge 113+ 中已稳定支持,Safari Technology Preview 也有较完整的实现。Firefox 正在积极开发中。

W3C 工作组还在推进以下扩展:

  • WebGPU Subgroups:支持线程组内共享操作,提升计算着色器效率。
  • WebGPU Ray Tracing:硬件光线追踪的 Web 封装。
  • WebGPU XR:与 WebXR 深度整合,为 VR/AR 场景提供低延迟渲染。
  • WebGPU Video:直接从视频元素导入纹理,减少拷贝。

总结

WebGPU 不是 WebGL 的"升级版",而是一次彻底的重建。它把现代 GPU 编程的最佳实践——显式管线、命令录制、计算着色器、类型安全的着色器语言——带入了 Web 平台。对于前端开发者,它打开了一扇通往高性能图形和计算的大门;对于 AI 从业者,它意味着浏览器内推理不再需要妥协。

如果你还没有尝试过 WebGPU,现在是个好时机。从一个三角形开始,逐步探索计算着色器,你会发现自己能做的事情比 WebGL 时代多了几个数量级。

评论