WebGPU 入门:在浏览器中释放 GPU 计算能力
引言
WebGPU 是下一代浏览器图形 API,被视为 WebGL 的继任者。与 WebGL 不同,WebGPU 不仅仅用于渲染,它还暴露了 GPU 的通用计算能力(GPGPU),让开发者可以在浏览器中直接利用 GPU 进行并行计算。这意味着从机器学习推理到物理模拟,许多原本需要在服务器端运行的工作负载,现在可以在用户的设备上完成。
本文将带你了解 WebGPU 的核心概念、与 WebGL 的关键区别、基本用法,以及一个实际的 GPU 计算示例。
WebGPU vs WebGL:不只是升级
WebGL 基于 OpenGL ES,设计于 2011 年前后,其架构深深植根于固定管线和旧式 GPU 编程模型。WebGPU 则基于现代图形 API(Vulkan、Metal、Direct3D 12),更贴近当代 GPU 的实际工作方式。
主要区别
- 显式管线模型:WebGPU 要求开发者预先定义渲染管线(Pipeline),包括着色器、混合状态、拓扑结构等,然后复用。WebGL 则是在每帧设置状态,开销更大。
- 命令编码:WebGPU 使用命令缓冲区(Command Buffer),先录制命令再一次性提交,减少 CPU-GPU 通信开销。
- 通用计算支持:WebGPU 原生支持计算着色器(Compute Shader),无需借助 WebGL 的 GPGPU hack。
- 类型安全:WebGPU 的 API 设计更加类型安全,WGSL(WebGPU Shading Language)比 GLSL 更加现代化。
核心概念速览
1. Adapter 与 Device
navigator.gpu.requestAdapter() 返回一个 Adapter,代表一个 GPU 设备的抽象。adapter.requestDevice() 则返回 Device,这是你与 GPU 交互的主要接口。你可以把 Device 理解为 GPU 的"句柄"。
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) throw new Error('WebGPU 不支持');
const device = await adapter.requestDevice();
2. Buffer
Buffer 是 GPU 上的内存块。你需要指定用途(如 Uniform、Storage、Vertex)和可见性(Vertex、Fragment、Compute 着色器是否可访问)。
const buffer = device.createBuffer({
size: 1024,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
3. Pipeline
管线分为渲染管线(RenderPipeline)和计算管线(ComputePipeline)。创建管线时需要提供着色器代码和布局信息。
4. Bind Group
Bind Group 是将 Buffer、Texture 等资源绑定到着色器的机制。你需要先创建 Bind Group Layout,再创建 Bind Group。
5. Command Encoder
命令编码器用于录制 GPU 命令。录制完成后调用 finish() 生成 CommandBuffer,再提交给队列执行。
实战:用 Compute Shader 做矩阵乘法
下面演示如何用 WebGPU 的计算着色器实现矩阵乘法。这是 GPGPU 的经典用例,也是理解 WebGPU 计算能力的最佳入门。
WGSL 着色器
@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, read_write> result: array<f32>;
let BLOCK_SIZE: u32 = 16u;
@compute @workgroup_size(16, 16)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let row = gid.x;
let col = gid.y;
// 假设 N x N 矩阵, N = 64
let N: u32 = 64u;
if (row >= N || col >= N) {
return;
}
var sum: f32 = 0.0;
for (var k: u32 = 0u; k < N; k = k + 1u) {
sum = sum + a[row * N + k] * b[k * N + col];
}
result[row * N + col] = sum;
}
JavaScript 端
async function matrixMultiply() {
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const N = 64;
const matrixA = new Float32Array(N * N);
const matrixB = new Float32Array(N * N);
// 填充随机数据
for (let i = 0; i < N * N; i++) {
matrixA[i] = Math.random();
matrixB[i] = Math.random();
}
// 创建 Buffer
const bufA = device.createBuffer({
size: matrixA.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
const bufB = device.createBuffer({
size: matrixB.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
const bufResult = device.createBuffer({
size: matrixA.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
const bufRead = device.createBuffer({
size: matrixA.byteLength,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(bufA, 0, matrixA);
device.queue.writeBuffer(bufB, 0, matrixB);
// 着色器代码
const shaderModule = device.createShaderModule({
code: shaderSource, // 上面的 WGSL 代码
});
// 计算管线
const pipeline = device.createComputePipeline({
layout: 'auto',
compute: { module: shaderModule, entryPoint: 'main' },
});
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: bufA } },
{ binding: 1, resource: { buffer: bufB } },
{ binding: 2, resource: { buffer: bufResult } },
],
});
// 编码命令
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(N / 16), Math.ceil(N / 16));
pass.end();
encoder.copyBufferToBuffer(bufResult, 0, bufRead, 0, matrixA.byteLength);
device.queue.submit([encoder.finish()]);
// 读取结果
await bufRead.mapAsync(GPUMapMode.READ);
const result = new Float32Array(bufRead.getMappedRange());
console.log('Result:', result);
bufRead.unmap();
}
性能注意事项
虽然 WebGPU 让浏览器具备了 GPU 计算能力,但并非所有场景都适合用它:
- 数据传输开销:CPU 和 GPU 之间的数据传输有成本。如果计算量太小,花在传输上的时间可能比计算本身还长。
- Workgroup 大小:合理设置 workgroup_size 对性能至关重要。通常选择 8×8 或 16×16,以匹配 GPU 的线程调度粒度。
- 内存对齐:Storage Buffer 的访问模式对性能影响很大。连续访问(coalesced)比随机访问快得多。
- 异步思维:所有 GPU 操作都是异步的。不要在性能敏感的循环中频繁创建 Buffer 或 Pipeline,应该在初始化时预创建。
浏览器兼容性
截至 2026 年,Chrome 和 Edge 已全面支持 WebGPU,Safari 也已在最新版本中落地支持,Firefox 仍在完善中。实际项目中建议使用特性检测:
if (!navigator.gpu) {
console.warn('WebGPU 不可用,回退到 WebGL 或 CPU 方案');
}
应用场景展望
WebGPU 的计算能力为 Web 端打开了许多新大门:
- 端侧 AI 推理:结合 TensorFlow.js 或 ONNX Runtime Web,可以在浏览器中运行小型模型推理,保护用户隐私且无需服务器算力。
- 实时物理模拟:粒子系统、流体模拟等可以充分利用 GPU 并行性。
- 图像/视频处理:滤镜、降噪、抠像等计算密集型任务可以在 GPU 上完成。
- 数据可视化:大规模数据点的渲染和交互计算可以更流畅。
总结
WebGPU 不只是 WebGL 的升级版,它是一个从设计理念到 API 风格都焕然一新的图形与计算平台。显式管线模型、命令缓冲、原生 Compute Shader 支持让它成为 Web 端高性能计算的重要基础设施。
对于前端开发者来说,现在是学习 WebGPU 的好时机。虽然学习曲线比 WebGL 陡,但它的表达能力和性能潜力都远超前辈。从计算着色器入手,逐步过渡到渲染管线,是一条推荐的路线。
随着浏览器支持的完善和工具链的成熟,WebGPU 有望成为 Web 端图形和计算的统一底层,让浏览器从"文档查看器"进化为真正的"应用平台"。