计算着色与图像处理
卷积、直方图、降噪是 数据并行公式,不是 getImageData 循环。场景图(源图 id、ROI、参数)留在 CPU;像素数组是投影。生产默认仍是 WebGL2 浮点 FBO ping-pong;真正的 compute 只在 requestAdapter() 成功后上 WebGPU。结果若每帧 readPixels 回 JS,GPGPU 被一次同步吃掉。
1. 场景怎么切
CPU:源 ImageBitmap / 视频帧句柄、kernel、ROI
↓ 上传一次
GPU:storage / 浮点纹理上跑公式
↓ 下一趟渲染直接采样
偶尔:缩小后再读回给 UI 数字
不要热路径 2D getImageData 做卷积再 texImage2D 回去。那是 Canvas 像素管线 的处理表面。
export async function tryComputeHistogram(
device: GPUDevice,
pipeline: GPUComputePipeline,
bindGroup: GPUBindGroup,
workgroups: number,
): Promise<void> {
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(workgroups);
pass.end();
device.queue.submit([encoder.finish()]);
}
export function hasFloatColor(gl: WebGL2RenderingContext): boolean {
return gl.getExtension("EXT_color_buffer_float") !== null;
}
export function runKernelPass(
gl: WebGL2RenderingContext,
readTex: WebGLTexture,
writeFbo: WebGLFramebuffer,
width: number,
height: number,
drawQuad: (gl: WebGL2RenderingContext) => void,
): void {
gl.bindFramebuffer(gl.FRAMEBUFFER, writeFbo);
gl.viewport(0, 0, width, height);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, readTex);
drawQuad(gl);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
离线卷积走 texel / UV,全屏三角形不要乘场景相机。只有「场景里一块处理板」才用世界矩形 × viewProj,kernel 仍在纹素空间。WebGL2 默认:全屏三角形 + 浮点附件,见 Compute 章。没 EXT_color_buffer_float 时不要用 RGBA8 假装积分。读写必须拆成 ping-pong。Transform feedback 适合粒子状态,不适合邻域卷积。
适用:全图像素同一 kernel、直方图、粒子全体积分且结果留在 GPU。
不适用:几十像素的 CPU 循环;每帧要把 1080p 直方图读回做 UI——缩小到 256 bin 或隔帧 mapAsync。
2. 哪层必须 CPU,哪层进 GPU
| 层 | 归属 |
|---|---|
| 选哪张源、kernel 系数、业务阈值 | CPU 文档 |
| texel / storage buffer | GPU 投影 |
| 「图上有哪些可点对象」 | CPU 场景,禁止从直方图反推 |
| 导出 PNG | 离线读 FBO;热路径禁止全屏读回 |
WebGPU:mapAsync 不要在 rAF 里 await,双 staging 隔帧,见 读回陷阱。adapter 为 null 是产品路径:降级浮点 FBO 或降级 CPU,不要 throw「请换浏览器」。
跨源源图无 CORS,compute 写完的表面一样 taint,readPixels / toBlob 仍失败,见 CORS。drawing buffer 原点左下,若把读回当 ImageData 必须翻 Y。
源图尺寸夹紧 MAX_TEXTURE_SIZE / WebGPU limits。上下文丢失后 storage 句柄作废,从 CPU 持有的 ImageBitmap 重建,不要假定 GPU 里还有「处理结果文档」。
3. 失败形态
| 症状 | 原因 |
|---|---|
| 结果阶梯状 | 8bit FBO 当浮点积分 |
| ping-pong 花屏 | 读写同一张纹理 |
| 算完比 CPU 还慢 | 每帧全屏 readPixels / rAF 里 await mapAsync |
| WebGPU 局域网挂 | 非安全上下文,或 adapter null 没降级 |
| 导出 SecurityError | 跨源纹理污染了目标 |
权威资料
- W3C — GPUComputePassEncoder
- W3C — GPUBuffer.mapAsync
- Khronos — EXT_color_buffer_float
- Compute 与 GPGPU
核对日期:2026-08-26