diff --git a/docker/frontend/src/engine/inpaint.ts b/docker/frontend/src/engine/inpaint.ts index 832c00c..d76db54 100644 --- a/docker/frontend/src/engine/inpaint.ts +++ b/docker/frontend/src/engine/inpaint.ts @@ -71,6 +71,23 @@ async function warmsUp(ort: typeof Ort, session: Ort.InferenceSession): Promise< } } +// Whether the page has a device of its own to run the graph on. Asked of the +// browser rather than of the runtime: creating a session of any kind — the +// WebGPU one included — builds the wasm backend underneath it, and +// `env.wasm.proxy` is read at that moment. A proxy set after it is a proxy that +// never took, and every run of the fallback then ends the way it did the first +// time this was tried: `no available backend found. ERR: [wasm] Error: worker +// not ready`. So the question is put before anything is created. +async function hasDevice(): Promise { + const gpu = (navigator as Navigator & { gpu?: { requestAdapter(): Promise } }).gpu; + if (!gpu) return false; + try { + return !!(await gpu.requestAdapter()); + } catch { + return false; + } +} + // WebGPU first, the processor as the answer to every way that can fail: no // adapter, no `shader-f16` needed here but an op the provider never // implemented, a driver that opens a session and dies on the first dispatch. @@ -99,6 +116,16 @@ function load(): Promise { ort.env.wasm.numThreads = self.crossOriginIsolated ? Math.min(4, navigator.hardwareConcurrency || 1) : 1; + // The fallback's turn is taken in a worker of the page's own, wherever + // there is no device to take it on: a wasm run is seconds of arithmetic + // that never hands the thread back in the middle of it, and a thread that + // cannot answer a finger for three seconds is a tab a phone takes away + // (measured: 3.5s of frozen page per repair, on a desktop). Proxied, the + // page keeps its frames and its touches while the repair is worked out, + // and the repair lands under the spinner the studio already shows. It is + // left off where a device is there, because the WebGPU path is a device of + // its own and must be built on the page's own thread. + ort.env.wasm.proxy = !(await hasDevice()); ort.env.webgpu.powerPreference = 'high-performance'; const session = await openSession(ort); return { ort, session };