Prompt and use case
A browser graphics app shows a black canvas after background resume, a driver update, or resource pressure. Design WebGPU device-loss handling: how to observe GPUDevice.lost, distinguish intentional destruction from transient loss, rebuild the device and every GPU resource, and prevent concurrent recovery flows from overwriting each other. Also cover unsupported browsers, permanently unavailable adapters, and graceful rendering fallback.
What the interviewer is testing
- Understanding
lostas a device-lifecycle Promise rather than an ordinary render error. - Distinguishing
destroyedfrom loss caused by browser, driver, or resource management. - Rebuilding adapters, devices, pipelines, buffers, textures, and bind groups.
- Designing single-flight recovery, stale-frame cancellation, and idempotent initialization.
- Handling secure contexts, Workers, compatibility, and observability.
Questions to clarify first
- Is the app a real-time canvas, editor, or offline compute job, and what recovery time is acceptable?
- Must CPU-side scenes, user input, and uncommitted edits survive?
- Which browsers, Workers, power states, and memory budgets are in scope?
- Should failure switch to WebGL, a static preview, or a reload prompt?
Thirty-second answer
I treat the GPU device as a replaceable session: CPU-side scene and resource descriptions are authoritative, while GPU objects are caches. After observing device.lost, a single-flight state machine pauses submissions and reads GPUDeviceLostInfo.reason; intentional destroyed ends the session, while other reasons trigger a new adapter and device, resource rebuilding, and render-loop recovery. Repeated failure or an adapter that returns already-lost devices enters a fallback path, with reason, recovery time, and failed resource recorded.
Deep-dive answer, step by step
1. Separate device and application state
Keep the scene graph, material parameters, geometry data, and texture sources on the CPU side. The device, queue, buffers, textures, pipelines, and bind groups are disposable GPU-session objects, not business truth.
2. Observe the lifecycle Promise
GPUDevice.lost stays pending during the device lifetime and resolves with GPUDeviceLostInfo after loss. Register one listener after initialization and send the callback into one recovery state machine.
3. Explain loss reasons
An explicit GPUDevice.destroy() normally corresponds to reason destroyed, so it should not trigger an unconditional restart. Browser resource management, driver updates, and transient device faults may recover; an unplugged or power-disabled adapter may keep returning already-lost devices.
4. Use a single-flight recovery gate
Return the same Promise from the recovery entry point instead of letting every animation frame initialize. Pause submissions, discard old command encoders, and use a generation token to reject asynchronous callbacks from the old device.
5. Rebuild resources from descriptions
Persist buffer usage, texture size and format, samplers, bind-group layouts, shader source, and pipeline configuration. After creating the new device, rebuild in dependency order: shaders and layouts, pipelines, buffers and textures, views, bind groups, then the canvas context and render loop.
6. Manage CPU data and budgets
Large textures and geometry can come from rereadable URLs, IndexedDB, or compressed caches. Upload in visibility- and priority-aware batches with a memory budget and cancellation signal, so recovery does not immediately recreate resource pressure.
7. Handle concurrency and visibility
Page hiding, Worker messages, and user retries can all request recovery. Constrain the state machine to ready, lost, recovering, and degraded; only the current generation may submit frames. Delay expensive rebuilding while the page is hidden.
8. Design fallback and observability
Check the secure context and navigator.gpu before requesting an adapter and device. After repeated failure, permanent device loss, or unsupported browsers, switch to WebGL, a static preview, or a clear reload prompt. Record the reason, adapter information, attempts, duration, failed resources, and fallback ratio without exposing internal errors to users.
Trade-offs and boundaries
Requesting a new device does not restore old buffers, textures, or pipelines; they must be rebuilt from CPU-side descriptions. Recovery cannot assume every loss is transient, and it must use backoff instead of infinite retries. WebGPU requires a secure context and remains limited availability; Workers can use the API, but browser and driver differences belong in the release matrix.
Rollout plan and evidence
- Build a CPU resource manifest and a GPU resource factory whose creation steps are repeatable.
- Add a generation and single-flight recovery state to the device, adapter, canvas context, and render loop.
- Inject intentional destruction, background resume, driver reset, memory pressure, and already-lost adapter cases.
- Verify resource order, user-edit preservation, frame pausing, and fallback UI, including Workers.
- Use MDN's
lostPromise,GPUDeviceLostInfo, secure-context, and limited-availability notes as the compatibility evidence.
Common mistakes and follow-ups
Mistake 1: Only calling requestDevice again
The new device has no old resources. Rebuild pipelines, buffers, textures, views, and bind groups from CPU descriptions.
Mistake 2: Retrying every reason automatically
Intentional destruction may be normal shutdown; retrying a permanently unavailable adapter creates a loop. Branch on the reason with backoff.
Mistake 3: Starting recovery on every animation frame
Concurrent flows race on shared state. A single-flight Promise and generation gate ensure one current session.
Mistake 4: Treating GPU objects as business state
Device loss clears the GPU session. Keep scenes, user input, and resource sources independent of the device.
Mistake 5: Ignoring unsupported and fallback paths
WebGPU is not Baseline and is available only in secure contexts. Detect it early and provide WebGL, a static preview, or a reload plan.