3 个新函数把顶点和索引塞进同一个 buffer:WGSL 的 buffer_view 扩展进了 Chrome 154,解包终于不用手算字节偏移
过去在 WGSL 里想干一件事:把同一个 storage buffer 里的数据按不同结构读出来,你只能靠手算字节偏移 + bitcast,或者干脆把数据拆成好几个 buffer、挂多个 @binding。Chrome 154(2026-09-15 发布)给 WGSL 加了一个 buffer_view 语言扩展,把这件事变成三个内置函数。
先说清它解决了什么。一个最常见的场景是「顶点 + 索引塞进同一块 buffer」:前 4 个字节是索引个数,接着是索引数组,然后是顶点数组。以前要么给这三段分别建 buffer,要么在着色器里用一长串 bitcast<...>(&data[i]) 手动切。buffer_view 的写法长这样:
requires buffer_view;
// +-------------------+-----------------------------+----------------------------+
// | Byte 0 .. 3 | Byte 4 .. (4 + N - 1) | Byte (4 + N) .. End |
// +-------------------+-----------------------------+----------------------------+
// | indices_size (u32)| indices (array<u32, count>) | vertices (array<f32>) |
// +-------------------+-----------------------------+----------------------------+
@group(0) @binding(0) var<storage> indices_and_vertices : buffer;
fn unpack_and_process_geometry() {
let indices_size = *bufferView<u32>(&indices_and_vertices, 0);
let indices = bufferArrayView<array<u32>>(&indices_and_vertices, 4, indices_size);
let vertices = bufferView<array<f32>>(&indices_and_vertices, indices_size + 4);
...
}
三个函数各管一段:bufferView<T>(&buf, offset) 把某个字节偏移处的内存重解释成指向 T 的指针;bufferArrayView<array<T>>(&buf, offset, count) 生成带显式元素数量的限定数组指针;bufferLength(&buf) 返回缓冲区字节长度,用来做边界检查。它们的共同点是——只在 uniform / storage / workgroup 变量上工作,并且必须用这三个内置函数访问重解释后的类型,不能随便 bitcast 绕过,这是为了让编译器和驱动能安全优化。
真正的 before / after 差距在「类型双关」这类需求上。比如你一个 storage buffer 里混放了 u32 头信息和 f32 数据,以前要写:
let raw = &data[i];
let header = bitcast<u32>((*raw));
let value = bitcast<f32>((*raw));
而现在:
let header = *bufferView<u32>(&data, 0);
let value = *bufferView<f32>(&data, 4);
不需要再手动对齐、不用为每段数据挂一个 binding。代码量从「按字节切三段」压到「三个函数一行一个」。
有一个必须注意的坑:这是语言扩展,不是默认可用。用之前必须在 JavaScript 侧检测,并在着色器顶部声明 requires:
if (!navigator.gpu.wgslLanguageFeatures.has("buffer_view")) {
throw new Error("WGSL buffer_view is not available");
}
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const shaderModule = device.createShaderModule({
code: `
requires buffer_view;
// ...用 bufferView() 的着色器
`,
});
requires 指令的作用是显式声明「这段着色器依赖某扩展,别在不支持的环境里静默跑错」。对不支持的浏览器,要么走降级分支,要么直接抛错。这也是为什么它跟随 Chrome 154 一起进,但 Safari / Firefox 需要等各自适配——发布意向里 WebKit 给了 positive,Firefox 尚无 signal。
落到你自己的项目,第一步很具体:打开 Chrome 154+,在 navigator.gpu.wgslLanguageFeatures 里查有没有 buffer_view,如果有,就把你现在那段「多个 binding + 手动 bitcast」的几何解包代码换成 bufferArrayView,然后用 bufferLength 补一个越界断言。先在一个 buffer 上验证输出和旧实现逐帧一致,再铺开到全部。
评论区
登录后可评论。