A GPUCommandEncoder records copies and passes without executing them; finish() seals them into a GPUCommandBuffer, which can be submitted only once ("cannot be submitted more than once", says Chrome 154 1 ); and device.queue.submit() hands buffers to the GPU in order. The specification describes three timelines: the content timeline of your script, the device timeline where the implementation creates and validates objects ("can live in a separate OS process"), and the queue timeline where "draw, copy, and compute jobs ... run on the GPU". Results come back only through promises:
<script type="module">
const t0 = performance.now();
const log = (m) => console.log(`${(performance.now() - t0).toFixed(1).padStart(5)} ms ${m}`);
const device = await (await navigator.gpu.requestAdapter()).requestDevice();
log('device ready');
const { COPY_SRC, COPY_DST, MAP_READ } = GPUBufferUsage;
const src = device.createBuffer({ size: 4, usage: COPY_SRC | COPY_DST });
const dst = device.createBuffer({ size: 4, usage: MAP_READ | COPY_DST });
device.queue.writeBuffer(src, 0, new Uint32Array([6])); // BookNest's six books
const encoder = device.createCommandEncoder();
encoder.copyBufferToBuffer(src, 0, dst, 0, 4); // recorded, not executed
device.queue.submit([encoder.finish()]);
log('command buffer recorded and submitted');
const done = device.queue.onSubmittedWorkDone()
.then(() => log('onSubmittedWorkDone() resolved'));
const read = dst.mapAsync(GPUMapMode.READ)
.then(() => log(`mapAsync() resolved: ${new Uint32Array(dst.getMappedRange())[0]}`));
log('JavaScript keeps running');
await Promise.all([done, read]);
</script>314.7 ms device ready 316.0 ms command buffer recorded and submitted 316.3 ms JavaScript keeps running 327.7 ms onSubmittedWorkDone() resolved 327.9 ms mapAsync() resolved: 6
Recording and submitting took under a millisecond; the copy's result arrived about 11 ms later, after a round trip through the GPU process (timings from a shared machine, so read them as orders of magnitude). Never wait on the GPU inside a frame: submit, keep going, and use results when their promise resolves.