Articles
WebGPU Interview Questions: Buffers, Bind Groups, and Synchronization
Practice WebGPU interview questions with buffer usages, bind groups, WGSL byte layouts, queue submission, mapped readback, and resource-lifecycle checks.
A compute shader runs without producing the expected numbers. The host uploaded the right values, but the shader reads a different offset. Another version gets the bytes right and fails during readback because the buffer remains mapped. WebGPU interview questions require a resource and ownership trace, not just a shader formula.
Follow one small computation from CPU bytes through a bind group, command submission, copy, and mapped readback. That trace separates layout mistakes, validation failures, and synchronization mistakes that can otherwise look like the same incorrect result.
Evidence boundary: The WebGPU and WGSL specifications and Mozilla's API documentation establish the software facts linked below. The byte ledger and buffer workflow are original exercises and engineering inference. No candidate reports or employer-specific interview frequencies are claimed. Begin with Explain shader compilers and graphics APIs and identify which contract belongs to the shader and which belongs to the host API.
What does creating a buffer establish?
Specification fact: WebGPU buffers have an allocated byte size and declared usages. Operations validate those usages and other constraints; creating a buffer does not grant every possible operation on it. See the WebGPU specification.
Original compute task: The CPU supplies an array of float32 values. A compute shader doubles each valid element into an output array. The CPU then reads that output and compares it with a reference result.
The input storage buffer needs the usages required for shader access and the selected upload method. For queue writes, include COPY_DST. The output storage buffer needs STORAGE for the shader and COPY_SRC for the later copy. A separate staging buffer needs COPY_DST and MAP_READ for CPU readback.
Official API example: Mozilla documents this output-and-staging pattern in GPUBuffer. Mapping is a state transition, not a request to inspect arbitrary GPU memory without regard to ownership.
Treat usage flags as part of the design. Adding every flag is not a universal solution because mapping combinations have restrictions. A shader storage allocation and a CPU-readable staging allocation serve different purposes.
How do bind groups connect resources to a shader?
A WGSL resource declaration identifies its group and binding. The host pipeline layout describes compatible resource types at those locations. A bind group supplies actual resources and ranges for that layout, and the pass selects the group before dispatch.
Official API fact: Creating a bind group checks its entries against the layout, including binding identifiers and resource compatibility. See createBindGroup.
Original binding ledger: Group zero, binding zero supplies read-only input storage; binding one supplies writable output storage. The shader declarations, bind-group layout, and host entries must agree on those numbers and access types.
A correct buffer at the wrong binding is still the wrong resource. A correctly bound buffer with a too-small range is another error. State the bound offset and size rather than referring only to the underlying allocation's total capacity.
If the pipeline uses automatic layout inference, obtain the corresponding bind-group layout from that pipeline and respect its compatibility. Do not assume every independently inferred layout is reusable across unrelated pipelines merely because the WGSL declarations look similar.
Dynamic offsets are an additional alignment and range question. Read the device's relevant limits and the selected binding's rules. The alignment of a WGSL member inside a struct is different from a required host binding offset alignment.
What does a WGSL struct require in host memory?
Specification fact: WGSL defines alignment, size, member offsets, and array stride for host-shareable data. A three-component float vector has alignment sixteen bytes and size twelve bytes; those quantities are different. See WGSL alignment and size.
Original storage-layout exercise: Consider this struct without custom member attributes:
struct Record {
position: vec3<f32>,
weight: f32,
uv: vec2<f32>,
flag: u32,
}
| Member or region | Byte offset | Occupied bytes | Reason |
|---|---|---|---|
| position | 0 | 12 | vec3 alignment is 16; size is 12 |
| weight | 12 | 4 | Scalar can follow the vector's twelve bytes |
| uv | 16 | 8 | vec2 alignment is 8 |
| flag | 24 | 4 | u32 alignment is 4 |
| End padding | 28 | 4 | Struct size rounds up to alignment 16 |
The struct's size is 32 bytes, so an ordinary storage array of these records has stride 32. Placing each next record at offset 28 would make the second record's position misaligned and change how the shader interprets the bytes.
A Float32Array is not enough to express the integer flag correctly by assigning the numeric value as a float. Use appropriately typed views or DataView writes into a shared ArrayBuffer. The bits for float32 one and uint32 one are different.
Engineering inference: Write a host packing function and a byte-level fixture before testing a complex shader. Uniform-address-space requirements can add constraints beyond a storage layout. Do not reuse the table as a universal layout for every address space or nested type.
What does the CPU-to-GPU ownership ledger look like?
Original workflow: Create the resources, upload input, encode dispatch and output copy, submit commands, request staging mapping, read and copy mapped bytes, then unmap before staging reuse.
| Stage | Resource state or operation | Common wrong assumption |
|---|---|---|
| Upload | CPU source copied through the chosen upload path | A typed array becomes permanently shared GPU storage |
| Encode | Commands recorded with resources and ranges | Encoding means computation already completed |
| Submit | Command buffers queued | Returning from submit means CPU can read results |
| Map | Await readback mapping | Waiting for an arbitrary timeout is synchronization |
| Read | Copy required bytes into CPU-owned storage | Mapped views survive unmap indefinitely |
| Reuse | Unmap before GPU use | One buffer can remain CPU-mapped during GPU access |
Official mapping fact: mapAsync returns a promise that resolves when the requested mapping is ready. getMappedRange supplies the mapped bytes, and unmap releases the mapping. See mapAsync.
Copy data you need to keep before unmapping. A typed-array view into mapped memory is not an independent saved result. Treat it as a borrowed view whose lifetime is controlled by the buffer's map state.
Specification fact: Mapping validation includes alignment, range, mode, and buffer-state requirements. A mapped buffer is unavailable for GPU use until unmapped. Check the exact operation contract in the buffer specification.
Do you need to await the entire queue before every readback?
Official API fact: onSubmittedWorkDone resolves when previously submitted queue work has completed. Mapping has its own readiness promise. See onSubmittedWorkDone.
Engineering inference: For a staging-buffer readback, await the mapping required for that buffer rather than adding a global queue drain without a specific dependency. A queue-wide wait can be useful for a particular scheduling or shutdown purpose, but it can unnecessarily serialize independent work.
No arbitrary sleep guarantees completion. A slow device, another workload, or a larger input can invalidate a timeout that appeared sufficient on one run. Wait on the API event that establishes the required condition.
Queue sequencing and CPU promise settlement are related but distinct. The specification provides particular ordering guarantees; it does not promise that all asynchronous operations settle in their invocation order. Avoid using the order of unrelated promises as an implicit dependency.
For repeated queries, a ring of staging buffers can let one result be consumed while later work proceeds. Define which buffer is mapped, queued, or free. Reusing the same staging allocation before its previous consumer releases it creates a lifecycle problem even when the computation is correct.
How does dispatch size create correctness questions?
Original exercise: A workgroup contains 64 invocations and the array has 130 elements. Dispatch three workgroups, producing 192 possible invocation indices. The shader should guard indices beyond the logical length before performing the intended load or store.
The allocation capacity and logical element count may differ. A padded buffer can avoid some bounds failures while still processing elements the application never meant to include. Pass or derive the intended length deliberately.
A workgroup barrier coordinates invocations within a workgroup under its specified memory semantics. It is not a device-wide barrier between all dispatched workgroups. Algorithms requiring global phases generally need an appropriate multi-dispatch design or another justified synchronization strategy.
Do not put a barrier behind control flow that only some invocations take unless the shader's rules permit that arrangement. Bounds handling and barrier placement interact: early return is easy for independent doubling, but a shared-memory algorithm may require all workgroup invocations to participate in synchronization.
State which values each invocation owns. Two invocations writing the same non-atomic location can introduce a race. A deterministic CPU reference does not make a racy GPU implementation deterministic.
What evidence should a failed computation produce?
Engineering inference: Label buffers, pipelines, and passes so validation messages identify the resource. Inspect shader compilation information, use appropriately scoped validation error handling, and handle device loss. Preserve the input size, usage flags, binding ranges, layout, and dispatch dimensions.
Treat an unsupported adapter, a rejected device request, and a lost device as different states. Check the actual features and limits available before requesting optional capabilities. Do not infer support from the presence of a single global API object.
An empty output can come from an invalid binding, a dispatch that did not cover the data, stale bytes, or a readback lifecycle error. Diagnose the earliest failed contract instead of changing without evidence shader arithmetic.
For a minimal doubling task, compare exact small integers represented in float32, then broaden to tolerances where operations require them. Include zero length where the host contract permits it, a partial final workgroup, and a second run with different input to detect stale output reuse.
The local checks accompanying this article verify the 32-byte packing and 130-element dispatch arithmetic. They do not execute a browser GPU shader or claim performance on a physical GPU. Those integration checks remain a distinct step for an implementation project.
What should a retry rebuild?
Engineering inference: After device loss, objects associated with that device do not become valid merely because a new device was requested. Recreate the needed buffers, shader modules, pipelines, and bind groups for the replacement device, then restore authoritative input data. Preserve application state separately from transient GPU objects.
For a failed compute request, record whether any output was accepted by downstream code. A retry should not silently append a second result or reuse an old mapped view. Associate the result with the input revision and operation identity so a consumer can distinguish a fresh computation from stale readback.
Keep a small known input for recovery verification. Successful resource creation alone does not establish that bindings, packing, dispatch, and output consumption are consistent again. Run the same reference-result check through the reconstructed path.
Five questions for adjacent implementation skills
The current verified bank has no direct WebGPU question set. These links support shader contracts, GPU testing, harness design, service lifecycle, and memory boundaries; they are explicitly adjacent practice, not direct WebGPU candidate reports.
| Question | WebGPU follow-up |
|---|---|
| Explain shader compilers and graphics APIs | Separate shader compilation and API validation. |
| Design a Dockerized GPU test pipeline | Record environment and GPU test evidence. |
| Implement a Python test harness | Compare bytes and outputs with a reference. |
| Design a GPU inference API | Design cancellation and resource lifecycle. |
| Explain Virtual Memory Beyond Physical RAM | Distinguish host memory from device resources. |
Continue with Design a Dockerized GPU test pipeline. Build a small reference-result test and preserve validation evidence before measuring throughput.