这是一篇沉浸式长文,建议在专属页面阅读 → 进入完整版
这是 Field Note 系列的第九篇,姐妹篇有 《一行 JS 的一生 · QuickJS 源码详解》(一个引擎全栈)、《V8 是怎么把 JS 跑快的》(多层 JIT)、以及 《一段内存的多重死亡 · 11 个 GC 家族的家谱》(横向跨语言)。本篇纵向切开一个全栈——从一行 JS API 走到一条 GPU 指令。
主线:1M 浮点平方 · 40 行 JS · 8 层翻译
const buf = device.createBuffer({ size: 4 * 1048576, usage: STORAGE | COPY_DST });
const shader = device.createShaderModule({ code: `
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
data[gid.x] = data[gid.x] * data[gid.x];
}`});
pass.dispatchWorkgroups(16384); // ⭐ 这一行要被翻译 8 次
这一行 dispatchWorkgroups 在到达 GPU 核之前的旅程:
- ① JS → WebIDL(Blink) · <1 µs
- ② Wire 序列化(Dawn Wire client) · ~5 µs
- ③ Mojo IPC(Renderer → GPU process) · ~30 µs
- ④ Dawn 验证 + state tracking · ~10 µs
- ⑤ Tint 编译(WGSL → MSL/HLSL/SPIR-V) · ~5 ms(首次) · 缓存后 0
- ⑥ 原生 API 调用(Metal/D3D12/Vulkan) · ~20 µs
- ⑦ 驱动编译(→ GPU ISA) · ~10 ms(首次) · 缓存后 0
- ⑧ GPU 命令处理器 dispatch · ~200 µs
总计:暖缓存 ~450 µs / 冷缓存首次 ~15 ms。
25 章 · 7 部分
- I · 背景(5 章):五个公式、十二年家谱、为什么不直接 Vulkan、设计哲学、三栈全景
- II · 主线:一个 compute 工作流
- III · API 表面(5 章):Adapter & Device · Buffer/映射/生命周期 · WGSL · Pipeline & BindGroup · Encoder & Queue
- IV · 跨进程(3 章):Renderer ↔ GPU process IPC · Validation/错误域 · 设备丢失与恢复
- V · 编译链(4 章):Tint(Dawn 前端)· Naga(wgpu 前端)· SPIR-V/MSL/HLSL 三后端 · Metal/D3D12/Vulkan 原生映射
- VI · 计算 & AI(4 章):Workgroup/Subgroup · matmul 优化 · transformers.js · FP16/atomics/timestamps
- VII · 综合(4 章):vs WebGL/WebCL 6 轴矩阵 · 生产故事簿 · 限制/指纹/安全 · 之后
真源码 · 真栈
- Dawn(Chrome,C++):
src/dawn/native/验证 ·src/tint/lang/wgsl/编译 - wgpu(Firefox + Deno + Bevy + Servo,Rust):
wgpu-core/·naga/src/back/ - WebKit(Safari):自家 WGSL→MSL(不是 Tint 也不是 Naga)
“WebGPU 不是 Vulkan-lite,而是另一个设计点——在不可信代码(JS)里安全暴露 GPU。同样的硬件,两种使用人群,两种 API 形状。”
Comments
0 comments