Buffer
GPU 上的线性内存块,存放顶点、索引、uniform 或存储数据。
创建方式
缓冲区由设备创建,不直接 new。最简单的方式是直接传一个类型化数组,
设备会按数组长度分配并以 VERTEX 用途初始化:
const positions = device.createBuffer(new Float32Array([
-0.5, -0.5, 0,
0.5, -0.5, 0,
0.0, 0.5, 0
]));
需要索引、写入或作为 uniform 使用时,用对象形式显式声明用途:
const vertexBuffer = device.createBuffer({
data: new Float32Array(vertexData),
usage: Buffer.VERTEX | Buffer.COPY_DST
});
const indexBuffer = device.createBuffer({
data: new Uint32Array([0, 1, 2]),
usage: Buffer.INDEX,
indexType: 'uint32'
});
const uniformBuffer = device.createBuffer({
byteLength: 64,
usage: Buffer.UNIFORM | Buffer.COPY_DST
});
构造参数
| 参数 | 说明 |
|---|---|
data | 初始数据,ArrayBuffer 或类型化数组视图 |
byteLength | 字节长度,未提供时由 data 推算 |
byteOffset | 写入起始偏移 |
usage | 用途位掩码,见下表 |
indexType | 索引类型:uint8、uint16、uint32 |
onMapped | 映射写入回调,用来避免一次额外拷贝 |
id | 调试用标识 |
用途标志
| 常量 | 含义 |
|---|---|
Buffer.VERTEX | 顶点属性缓冲 |
Buffer.INDEX | 索引缓冲 |
Buffer.UNIFORM | uniform 缓冲 |
Buffer.STORAGE | 存储缓冲,可用于计算着色器 |
Buffer.COPY_DST | 允许作为写入目标,更新数据时需要 |
Buffer.COPY_SRC | 允许作为读取来源 |
WebGL2 对这些用途的约束相对宽松,但同一份代码要跑在 WebGPU 上时,
用途声明必须齐全。养成把 VERTEX | COPY_DST 写全的习惯,
迁移时能省掉一轮排查。
属性与方法
| 成员 | 说明 |
|---|---|
byteLength | 缓冲区字节长度 |
usage | 创建时的用途掩码 |
updateTimestamp | 最后一次写入的时间戳,可用于判断是否需要重绘 |
write(data, byteOffset) | 覆盖写入数据 |
readAsync(offset, length) | 异步读回,返回 Uint8Array |
mapAndWriteAsync(cb, offset, length) | 映射后写入,避免中间拷贝 |
mapAndReadAsync(cb, offset, length) | 映射后读取,回调结束后解除映射 |
readSyncWebGL(offset, length) | 同步读回,仅 WebGL 可用,会阻塞 |
destroy() | 释放 GPU 内存 |
逐帧更新
// 创建时声明可写
const positions = device.createBuffer({
data: new Float32Array(count * 3),
usage: Buffer.VERTEX | Buffer.COPY_DST
});
function update(time) {
const data = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
data[i * 3] = xAt(i);
data[i * 3 + 1] = Math.sin(i * 0.1 + time) * 0.4;
data[i * 3 + 2] = 0;
}
positions.write(data);
}
每帧新建数组会产生垃圾回收压力。点数固定时,应当复用一个
Float32Array,原地改值后再写入。
DynamicBuffer
频繁部分更新的场景,DynamicBuffer 封装了环形缓冲与分段写入,
减少整体覆盖带来的带宽浪费。如果每帧只有一个 uniform 变化,
用它比每次重写整个缓冲区更合适。
读回数据
GPU 到 CPU 的读回是同步点,代价较高。readAsync 会等待 GPU 完成当前工作,
不能在每帧调用。需要做回读校验、截图或拾取结果分析时,
放在交互结束或调试流程里。
缓冲区不负责布局。Buffer 只是内存,怎么解释这段内存
由 bufferLayout 决定。同一个缓冲区可以按不同格式读取,
但格式必须与着色器声明一致。
经纬志(2026)。《Buffer》。https://www.jingweizhi.com/lumagl/api/buffer.html(访问日期 2026-11-03)。