跳转至

Uniform模块架构

Uniform Module 刻意保持微小:一个 UniformBuffer 类,几个类型辅助函数,以及 UniformUtils 中的少量函数。它们共同强制执行 WebGPU 的统一缓冲布局约束,并保持 CPU↔GPU 传输的可预测性。

设计目标

  1. 缓存优先 (Cache-first) – 在 CPU 上保留 Uniform 数据的 ArrayBuffer 副本,以便在一次 queue.writeBuffer 调用之前可以进行多次更新。
  2. 共享布局 (Shared layouts) – 暴露 UniformBuffer.bindGroupLayout(),以便每个管线都可以重用相同的统一绑定组布局(@binding(0) 统一缓冲,在 VS/FS/CS 中可见)。
  3. 对齐保证 (Alignment guarantees) – 提供辅助函数 (UniformUtils.alignSize, packVec, packMat4),以便相机/渲染设置结构体始终遵守 WebGPU 的 16 字节规则。
  4. 最小表面积 (Minimal surface area) – 没有花哨的池化或映射;只需构造一次并重用。

数据生命周期

new UniformBuffer(device, initBytes)
      ├─ 创建 GPU 缓冲 (UNIFORM | COPY_DST)
      ├─ 将初始字节复制到 CPU 缓存 (_data)
      └─ 使用 queue.writeBuffer 上传初始内容

setData(view)
      ├─ 将字节复制到 _data (CPU 缓存)
      └─ 数据已更新但尚未同步到 GPU (隐式"脏"状态)

flush(device?)
      └─ dev.queue.writeBuffer(buffer, 0, _data)

destroy()
      └─ buffer.destroy()
  • data getter 返回用于检查/调试的克隆 ArrayBuffer
  • dataBytes setter 整体替换缓存(当 ONNX 通过暂存缓冲覆盖模型参数 Uniform 时使用)。
  • clone() 简单地使用缓存的字节构造另一个 UniformBuffer

内存布局说明

WebGPU 要求统一缓冲为 16 字节对齐,并遵守类似 std140 的填充规则:

  • 标量占用 4 字节,但如果位于结构体中,仍位于 16 字节槽内。
  • vec2 – 8 字节,vec3 – 填充到 16 字节(被视为 vec4)。
  • mat4x4 以列主序存储,总共 64 字节。

UniformUtils 负责处理这些细节:

UniformUtils.alignSize(100);           // → 112
UniformUtils.packVec([1,2,3], 3);      // → Float32Array([1,2,3,0])
UniformUtils.packMat4(matrix16);       // 确保 16 个元素
UniformUtils.createAlignedBuffer(n);   // 返回填充到 16 字节的 ArrayBuffer

绑定组布局

UniformBuffer.bindGroupLayout(device) 总是返回相同的布局:

@group(N) @binding(0) var<uniform> ...;

可见性位覆盖 VS/FS/CS,因此相同的布局适用于预处理、排序和渲染管线。每个 UniformBuffer 使用该共享布局构造自己的 BindGroup

集成模式

相机 + 渲染设置

GaussianRendererGaussianPreprocessor 各自拥有两个 Uniform:

  • cameraUniforms (272 字节: view, viewInv, proj, projInv, viewport, fov)
  • settingsUniforms (~80 字节: 裁剪框, 高斯缩放, SH 阶数, 开关)

在编码计算或渲染 Pass 之前,它们调用 setData(...) 随后调用 flush()

模型参数

每个 PointCloud 构造一个 modelParamsUniforms = new UniformBuffer(device, 128-byte struct),其镜像了 WGSL 的期望(变换、偏移量、缩放、精度元数据)。预处理通过 updateModelParamsBufferupdateModelParamsWithOffset 更新此缓冲,并在分派计算之前 flush。

ONNX 计数器

动态点云传递一个额外的 countBuffer;预处理 flush 模型参数 Uniform,然后使用 copyBufferToBuffer 覆盖字节偏移量 68 处的 num_points 槽位。由于 Uniform 缓存镜像 GPU 数据,渲染器稍后可以检查 modelParamsUniforms.data 进行调试。

错误检查

  • setDataview.byteLength !== size 时抛出异常,防止意外的部分写入。
  • dataBytes setter 也强制执行相同的大小检查。
  • packMat4 除非正好提供 16 个元素,否则抛出异常。

未来钩子

当前的实现是有意简化的。如果我们将来需要缓冲池或持久映射,我们可以扩展 UniformBuffer 以接受自定义用法标志(已通过 UniformConfig 支持)并添加 UniformPool。目前,简单性使得每帧 Uniform 更新变得琐碎且易于推理。