阶段 4 · 计算基础18 分钟
GPU 读回:Copy、Map 与行对齐
MAP_READ、copyTextureToBuffer 与异步同步点
把离屏纹理复制到可映射缓冲区,处理 bytesPerRow 对齐,再从 CPU 读取像素。
LEARNING GOALS
先明确本课目标
1
创建 readback buffer
2
理解 256 字节 bytesPerRow 对齐
3
正确 map/unmap
MENTAL MODEL · 再建立心智模型
GPU 资源通常不可直接被 JavaScript 读取;先复制到 MAP_READ staging buffer,等待完成后 mapAsync。
MINIMAL EXAMPLE
为什么 GPU Buffer 不能像普通数组一样直接读取?
GPU 资源位于 GPU 可访问的内存域。要让 CPU 读取,通常先复制到带 MAP_READ | COPY_DST 的 staging buffer,再异步 mapAsync 等待 GPU 完成。
javascriptencoder.copyBufferToBuffer(
gpuResult, 0,
readbackBuffer, 0,
byteLength,
);
device.queue.submit([encoder.finish()]);
await readbackBuffer.mapAsync(GPUMapMode.READ);
const copy = readbackBuffer.getMappedRange().slice(0);
readbackBuffer.unmap();- 先复制 计算结果 Buffer 通常没有 MAP_READ usage,需复制到 staging buffer。
- mapAsync 是同步点 Promise 解析前 CPU 不能读取;频繁等待会破坏并行流水线。
- unmap 映射期间 Buffer 不能同时被 GPU 使用,读完应及时解除映射。
CPU 获得一份稳定字节副本,但付出了 GPU→CPU 同步与传输成本。
OPTIONAL · 按需查阅完整术语表与概念边界遇到陌生术语时再展开,不打断主线实验。
展开⌄
资源GPUBufferUsage在创建缓冲区时声明允许用途的位标志;未声明的用途后续不能使用。内存布局Alignment数据字段、绑定偏移和复制行距必须满足的字节边界要求。错误对齐会导致验证失败或数据解释错误。数据传输GPU Readback把 GPU 结果复制到 MAP_READ 缓冲区并异步映射给 JavaScript,通常是昂贵同步点。数据传输mapAsync等待缓冲区可由 CPU 访问并映射其字节范围;映射期间不能被 GPU 使用。数据传输bytesPerRow纹理与缓冲区复制时的行跨度,copyTextureToBuffer 常要求 256 字节对齐。数据传输MAP_READGPUBufferUsage 标志,允许 Buffer 通过 mapAsync(GPUMapMode.READ) 映射供 CPU 读取。数据传输copyTextureToBufferGPUCommandEncoder 的复制命令,把纹理子资源按指定 bytesPerRow 布局写入 Buffer。数据传输Staging Buffer用于在 GPU 专用资源与 CPU 可映射内存之间中转数据的缓冲区;读回时先复制到它,再等待并 map。
LIVE EXPERIMENT · 先获得反馈
边改边运行
浏览器加载后会激活代码编辑器、独立运行环境与实时输出。
本课挑战
实现三缓冲异步截图队列。
回到原理,解释刚才发生了什么
先用结果建立反馈,再按运行流程、核心原理和知识关系逐层深入。
CORE IDEA
核心讲解
读回是同步边界
mapAsync 必须等 GPU 完成相关写入,可能阻塞流水线。实时程序应减少频率、缩小区域或使用多缓冲隐藏延迟。
行对齐
纹理到缓冲区复制的 bytesPerRow 通常要求 256 字节倍数,即使实际每行像素数据远小于此。
OPTIONAL · 迁移时查阅WebGPU 与 WebGL2 对照主线实验完成后,再用完整映射处理跨平台或跨 API 迁移。
展开⌄
两者都应避免同步回读;WebGPU 把复制、等待与映射拆成显式阶段。
WebGL
readPixels/getBufferSubData 可能阻塞;可配合 PIXEL_PACK_BUFFER 与 fenceSync 降低立即等待。
WebGPU
先 copy 到 MAP_READ Buffer,提交后 mapAsync,在映射完成时读取 CPU 字节。
异步 API 不会自动消除同步成本;仍应做环形 staging buffer,并晚几帧消费结果。