WebGPU 入门指南:在浏览器中释放 GPU 计算能力
WebGPU 入门指南:在浏览器中释放 GPU 计算能力
WebGPU 是下一代浏览器图形 API,被设计为 WebGL 的继任者。与 WebGL 相比,WebGPU 不仅在渲染性能上有了显著提升,更重要的是它直接暴露了 GPU 的通用计算能力(GPGPU),让开发者可以在浏览器中编写高性能的并行计算程序。本文将带你从零开始理解 WebGPU 的核心概念,并通过实际代码示例感受它的强大之处。
一、为什么我们需要 WebGPU?
WebGL 自 2011 年发布以来,已经深刻地改变了 Web 生态。从 3D 游戏到数据可视化,WebGL 让浏览器成为了图形渲染的重要平台。然而,WebGL 本质上是 OpenGL ES 的浏览器封装,其架构设计几十年前就已定型,难以充分利用现代 GPU 的能力。
WebGL 存在几个核心痛点:
- API 抽象层级过高: WebGL 将 GPU 操作封装为固定管线的状态机,开发者无法直接控制 GPU 的工作调度。
- 计算着色器缺失: WebGL 2.0 虽然引入了 Transform Feedback,但仍然不支持真正的通用计算着色器(Compute Shader),GPGPU 只能通过「把数据塞进纹理、用顶点着色器模拟计算」这种 hacky 的方式实现。
- 性能瓶颈: 每次状态切换都有显著开销,Draw Call 的 CPU 侧成本很高,难以实现 CPU 与 GPU 之间的高效协同。
WebGPU 直接映射了现代图形 API(Vulkan、Metal、Direct3D 12)的设计理念,提供了更接近底层的 GPU 控制能力,同时保持了 Web 平台的安全性要求。
二、WebGPU 的核心架构
WebGPU 的编程模型与 WebGL 截然不同,理解以下几点是掌握 WebGPU 的关键:
1. 适配器与设备(Adapter & Device)
在 WebGPU 中,GPUAdapter 代表一个物理 GPU 适配器,而 GPUDevice 是从适配器请求的逻辑设备。所有 WebGPU 操作都从获取设备开始:
// 请求 GPU 适配器
const adapter = await navigator.gpu.requestAdapter({
powerPreference: 'high-performance'
});
if (!adapter) {
console.error('当前浏览器不支持 WebGPU');
return;
}
// 请求 GPU 设备
const device = await adapter.requestDevice({
requiredFeatures: ['shader-f16'],
requiredLimits: {
maxStorageBufferBindingSize: 32 * 1024 * 1024,
}
});
这种设计允许浏览器在多 GPU 设备(如笔记本的集成显卡 + 独立显卡)之间做出合理的选择,也允许开发者声明所需的特性级别。
2. 命令缓冲区(Command Buffer)
WebGPU 采用了「录制-提交」的命令模型。你不会像 WebGL 那样即时地一条条调用 GPU 命令,而是将所有命令录制到 GPUCommandEncoder 中,然后一次性提交给 GPU 执行:
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginComputePass();
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatchWorkgroups(width / 8, height / 8);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
这种模式让 GPU 驱动能够更好地优化命令执行顺序,减少 CPU-GPU 通信的开销。
3. 着色器语言:WGSL
WebGPU 引入了新的着色器语言 WGSL(WebGPU Shading Language)。与 GLSL 不同,WGSL 是专门为 WebGPU 设计的,类型系统更加严格,语法更接近 Rust:
@group(0) @binding(0) var<storage, read> input_data: array<f32>;
@group(0) @binding(1) var<storage, read_write> output_data: array<f32>;
@compute @workgroup_size(8, 8)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let index = gid.x + gid.y * 1024u;
if (index >= arrayLength(&input_data)) {
return;
}
output_data[index] = input_data[index] * 2.0;
}
WGSL 的优势在于:编译器被内嵌在浏览器中,不需要开发者在运行时从 GLSL 编译为 SPIR-V;类型安全减少了运行时错误;模块化设计支持代码复用。
三、实战:用 WebGPU 实现矩阵乘法
矩阵乘法是深度学习中最核心的计算操作,也是展示 GPU 并行计算能力的经典案例。我们来看看如何用 WebGPU 的计算着色器实现一个高效的矩阵乘法。
步骤 1:创建缓冲区
const MATRIX_SIZE = 512;
// 创建输入矩阵 A 和 B(行优先存储)
const bufferA = device.createBuffer({
size: MATRIX_SIZE * MATRIX_SIZE * 4,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
const bufferB = device.createBuffer({
size: MATRIX_SIZE * MATRIX_SIZE * 4,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
// 创建输出矩阵 C
const bufferC = device.createBuffer({
size: MATRIX_SIZE * MATRIX_SIZE * 4,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
// 写入初始数据
const matrixA = new Float32Array(MATRIX_SIZE * MATRIX_SIZE);
const matrixB = new Float32Array(MATRIX_SIZE * MATRIX_SIZE);
// ... 填充随机数据 ...
device.queue.writeBuffer(bufferA, 0, matrixA);
device.queue.writeBuffer(bufferB, 0, matrixB);
步骤 2:编写 WGSL 计算着色器
const shaderCode = `
@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 SIZE: u32 = 512u;
@compute @workgroup_size(8, 8)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let row = gid.y;
let col = gid.x;
if (row >= SIZE || col >= SIZE) {
return;
}
var sum: f32 = 0.0;
for (var k: u32 = 0u; k < SIZE; k = k + 1u) {
sum = sum + a[row * SIZE + k] * b[k * SIZE + col];
}
result[row * SIZE + col] = sum;
}
`;
步骤 3:创建流水线并执行
const shaderModule = device.createShaderModule({ code: shaderCode });
const pipeline = device.createComputePipeline({
layout: 'auto',
compute: { module: shaderModule, entryPoint: 'main' }
});
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: bufferA } },
{ binding: 1, resource: { buffer: bufferB } },
{ binding: 2, resource: { buffer: bufferC } },
],
});
// 执行计算
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginComputePass();
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatchWorkgroups(MATRIX_SIZE / 8, MATRIX_SIZE / 8);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
在 512×512 的矩阵乘法中,WebGPU 的计算着色器可以轻松比纯 JavaScript 实现快 100 倍以上,这正是因为 GPU 的数千个核心可以同时处理不同的输出单元。
四、WebGPU 在 AI 推理中的应用
WebGPU 对 Web 端 AI 推理的意义是革命性的。目前主流的浏览器端 AI 框架如 TensorFlow.js 和 ONNX Runtime Web 已经开始支持 WebGPU 后端。与 WebGL 后端相比,WebGPU 后端在模型加载速度和推理延迟上都有数量级的提升。
以 ONNX Runtime Web 为例,只需几行代码即可启用 WebGPU 后端:
import * as ort from 'onnxruntime-web/webgpu';
const session = await ort.InferenceSession.create(modelUrl, {
executionProviders: ['webgpu'],
});
const results = await session.run({
input: new ort.Tensor('float32', inputData, [1, 3, 224, 224])
});
对于在浏览器中部署中等规模的 Transformer 模型(如 DistilBERT 或小型 GPT),WebGPU 后端可以让推理时间从「秒级」降低到「毫秒级」,使得真正可用的浏览器端 AI 应用成为可能。
更令人兴奋的是,社区已经出现了直接利用 WebGPU 计算着色器手写 Transformer 推理引擎的项目,如 llm.cpp 的 WebGPU 移植版本,可以在浏览器中运行量化后的大语言模型。虽然性能还无法与原生 CUDA 相比,但这种「零安装、零下载、打开浏览器即用」的 AI 体验代表了 Web 平台的未来方向。
五、浏览器兼容性与未来展望
截至 2026 年,WebGPU 已经在 Chrome、Edge 和 Safari 技术中获得支持,Firefox 也在积极实现中。ChromeOS 和 Android 平台上基于 Vulkan 的实现已经相当成熟,iOS/macOS 上基于 Metal 的实现也趋于稳定。
WebGPU 的路线图还包括一些令人期待的扩展特性:
- rayQuery / rayTracing: 光线追踪查询 API,让浏览器中的实时光线追踪成为可能。
- subgroups: 允许同一工作组内的线程进行高效的共享内存操作和数据交换,进一步提升计算密集型任务的性能。
- shader-f16: 原生 FP16 支持,对于 AI 推理可以减少一半的显存占用和翻倍的吞吐量。
- video textures: 直接将视频流作为纹理输入到 GPU 管线,加速视频处理和计算机视觉任务。
六、性能优化最佳实践
在 WebGPU 开发中,有几个关键的性能优化策略:
1. 减少缓冲区映射操作。通过 writeBuffer 和 copyBufferToBuffer 操作替代 mapAsync,避免阻塞主线程。
2. 合理设置工作组大小。大多数 GPU 的最大工作组大小是 256 个线程。workgroup_size(8, 8) 是一个常见的默认值,但根据具体 GPU 架构调整可能带来 20-30% 的性能提升。
3. 利用缓冲区对齐。GPU 缓冲区访问对对齐有严格要求,不正确的对齐可能导致性能惩罚或运行时错误。
4. 异步流水线创建。使用 device.createComputePipelineAsync 替代同步的 createComputePipeline,避免编译着色器时阻塞主线程。
结语
WebGPU 不是一个简单的图形 API 升级,它是 Web 平台 GPU 编程范式的根本性变革。从图形渲染到科学计算,从 AI 推理到视频处理,WebGPU 赋予了浏览器前所未有的 GPU 控制能力。虽然学习曲线比 WebGL 陡峭,但掌握 WebGPU 意味着你能在 Web 平台上实现过去只有原生应用才能达到的性能水平。
对于前端开发者来说,现在是开始学习 WebGPU 的最佳时机。官方规范、MDN 文档和各种社区教程正在快速完善,早期采纳者将在即将到来的 WebAI 浪潮中占据先发优势。不要等到所有浏览器都完美支持才开始 — 现在就动手写你的第一个计算着色器吧。