Skip to main content

Core GPU cookbook

These recipes are intentionally small. Follow the linked guide for the mental model and the linked reference for exact options and ownership.

GoalStart withResult
Open a portable GPUluma.createDevice()A WebGPU or WebGL 2 Device
Upload changing dataDevice.createBuffer()A reusable GPU allocation
Draw a frameRenderPassCommands targeting the current framebuffer
Run computeComputePassGPU-written storage buffers or textures
Read a small resultBuffer.readAsync()A copied CPU-visible byte range
Resize presentationCanvasContext.setDrawingBufferSize()A correctly sized canvas and attachments
Handle device lossDevice.lostA clear stop-and-recreate boundary

Initialize a portable device​

const device = await luma.createDevice({
type: 'best-available',
adapters: [webgpuAdapter, webgl2Adapter],
createCanvasContext: true
});
console.log(device.type); // 'webgpu' or 'webgl'

Import both adapters when both backends are acceptable. See GPU initialization.

Upload data​

const positions = device.createBuffer({
data: new Float32Array([0, 0, 1, 0, 0, 1]),
usage: Buffer.VERTEX | Buffer.COPY_DST
});
positions.write(nextPositions);

Declare every later use at creation. The owner eventually calls positions.destroy().

Render​

const renderPass = device.beginRenderPass({clearColor: [0, 0, 0, 1]});
renderPass.setPipeline(pipeline);
renderPass.setVertexArray(vertexArray);
renderPass.draw({vertexCount: 3});
renderPass.end();
device.submit();

Reuse the pipeline and vertex array; only encode the frame’s commands.

Run compute​

computePipeline.setBindings({data: storageBuffer});
const computePass = device.beginComputePass();
computePass.setPipeline(computePipeline);
computePass.dispatch(Math.ceil(itemCount / 64));
computePass.end();
device.submit();

Compute is WebGPU-only. Use GPU Core when several stages need dependency scheduling.

Read data back​

const bytes = await resultBuffer.readAsync(0, Uint32Array.BYTES_PER_ELEMENT);
const result = new Uint32Array(bytes.buffer, bytes.byteOffset, 1)[0];

Read back only bounded results. Mapping or copying a large result introduces a synchronization boundary.

Resize presentation​

device.canvasContext.setDrawingBufferSize(width, height);
depthTexture.destroy();
depthTexture = device.createTexture({width, height, format: 'depth24plus'});

Recreate size-dependent attachments, not static buffers, shaders, or pipelines.

Recover from validation or device errors​

device.lost.then(({message}) => {
stopRendering();
reportDeviceFailure({backend: device.type, message});
showRestartAction();
});

Never keep submitting against a lost device. Recreate all device-owned resources after obtaining a new one.