跳到主要内容

Compute与GPGPU

通用计算(直方图、粒子积分、前缀和、剔除)应优先 WebGPU compute。WebGL 没有计算着色器;用浮点 FBO 或 transform feedback 硬做 GPGPU,是带光栅税的兼容路径,不是默认架构。

1. 为什么 WebGPU 是首选

Compute pass 随机读写 storage buffer、有 workgroup 共享内存、能写 indirect draw 参数。没有「必须画一个 fullscreen triangle 才能干活」。

export async function dispatchHistogram(
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()]);
}

requestAdapter()null 时这条路不存在,降级见 上下文生命周期。不要把 compute 写成「没有 GPU 就 throw」的唯一路径。

适用:每帧要对全体粒子 / 全图像素做同一公式、且结果留在 GPU 给下一趟渲染用。
不适用:几十个点的 CPU 循环、要回读成 JS 再决策的热路径(mapAsync 同样是同步点)。

2. WebGL 能做什么、边界在哪

手法机制边界
浮点 FBO全屏三角形,FS 读上一张纹理、写下一张必须走光栅;无散写;要 EXT_color_buffer_float / EXT_color_buffer_half_float
Transform feedbackVS 写出 varyings 到 buffer适合粒子积分;不适合图像邻域卷积
整数 FBO 编码把 float 拆进 RGBA8精度差,调试痛苦
export function hasFloatColor(gl: WebGL2RenderingContext): boolean {
return gl.getExtension("EXT_color_buffer_float") !== null;
}

export function pingPong(
gl: WebGL2RenderingContext,
readTex: WebGLTexture,
writeFbo: WebGLFramebuffer,
drawQuad: (gl: WebGL2RenderingContext) => void,
): void {
gl.bindFramebuffer(gl.FRAMEBUFFER, writeFbo);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, readTex);
drawQuad(gl);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}

移动端 mediump 会把积分漂掉;浮点附件 incomplete 时不要假装算过。结果若 readPixels 回 CPU,GPGPU 的意义被一次同步吃掉——直方图展示用 WebGPU storage + 偶尔回读,或缩小后再读。

热路径不要:2D getImageData 做卷积再 texImage2D 回去。那是 Canvas 像素管线 的处理表面,不是 GPU 计算。

3. 数据所有权

场景图仍在 CPU。Compute 写的是 投影(粒子位置 buffer、直方图 bin)。业务删除一个发射器,要同时让 GPU buffer 失效或重打包,不要从 storage 反推「有哪些对象」。

4. 失败形态

症状原因
WebGL 「compute」结果阶梯状没浮点附件,用 8bit FBO 积分
ping-pong 花屏读写同一张纹理(反馈回路未拆)
粒子一帧后全 NaN移动端 mediump / 没夹速度
算完比 CPU 还慢每帧 readPixels 整张
WebGPU 示例局域网挂非安全上下文,或 adapter 为 null 没降级

权威资料

核对日期:2026-08-26