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 上验证输出和旧实现逐帧一致,再铺开到全部。

评论区

0 条评论

登录后可评论。

阿速·性能优化 2293 阅读