The Queue

The Queue and Submitting Command Buffers

Each device has one queue, device.queue (Encoders and Queues). Besides submit() and onSubmittedWorkDone(), it offers shortcuts that need no encoder: writeBuffer(), writeTexture() and copyExternalImageToTexture(). Its key guarantee is ordering: writes and submitted command buffers execute in the order you issue them, as this copy of one buffer before and after a price change shows:

Queue writes and submissions execute in issue orderHTMLLive
<script type="module">
  const device = await (await navigator.gpu.requestAdapter()).requestDevice();
  const { COPY_SRC, COPY_DST, MAP_READ } = GPUBufferUsage;
  const price = device.createBuffer({ size: 4, usage: COPY_SRC | COPY_DST });
  const before = device.createBuffer({ size: 4, usage: COPY_DST | MAP_READ });
  const after = device.createBuffer({ size: 4, usage: COPY_DST | MAP_READ });
  const copyTo = (dst) => { const e = device.createCommandEncoder();
    e.copyBufferToBuffer(price, 0, dst, 0, 4); return e.finish(); };
  device.queue.writeBuffer(price, 0, new Float32Array([14.99]));   // The Quiet Harbor
  device.queue.submit([copyTo(before)]);
  device.queue.writeBuffer(price, 0, new Float32Array([12.74]));   // 15% off
  device.queue.submit([copyTo(after)]);
  await Promise.all([before.mapAsync(GPUMapMode.READ), after.mapAsync(GPUMapMode.READ)]);
  const read = (b) => new Float32Array(b.getMappedRange())[0].toFixed(2);
  console.log(`first copy saw ${read(before)}, second copy saw ${read(after)}`);
</script>

Batch a frame's command buffers into one submit([...]), since each submission has a fixed cost.