The ping-pong pattern uses two alternating state buffers — one read-input, one write-output — to prevent in-place mutation corruption in GPU simulations
In GPU simulations, a compute shader cannot safely read from and write to the same buffer in the same dispatch because parallel invocations may overwrite data others have not yet read. The ping-pong pattern maintains two identically-sized state buffers (A and B): on odd steps read A, write B; on even steps read B, write A. Each step reads the fully-committed previous state. In WebGPU, implemented with two storage buffers and two bind groups that swap buffer roles, toggled by a step counter modulo 2.
Examples
const bindGroups = [
device.createBindGroup({ entries: [{ binding: 1, resource: { buffer: cellStateStorage[0] } }, { binding: 2, resource: { buffer: cellStateStorage[1] } }] }),
device.createBindGroup({ entries: [{ binding: 1, resource: { buffer: cellStateStorage[1] } }, { binding: 2, resource: { buffer: cellStateStorage[0] } }] }),
];
computePass.setBindGroup(0, bindGroups[step % 2]);
Assessment
Without ping-pong, why does a Game of Life compute shader that reads and writes the same buffer in one dispatch produce wrong results? Sketch the two-buffer solution at the bind-group level.