From 5aa6a5ba230fd3037bd3411a767f3ac009e34cfd Mon Sep 17 00:00:00 2001 From: 3dtours Date: Wed, 14 Oct 2026 08:59:13 +0700 Subject: [PATCH] fix(studio): a repair lands under the brush with no pill over it, and a browser with no device is not asked for one --- docker/frontend/src/App.tsx | 7 ++++++- docker/frontend/src/engine/inpaint.ts | 28 +++++++++++++++++++++------ docker/frontend/src/i18n/en.ts | 3 --- docker/frontend/src/i18n/vi.ts | 1 - 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/docker/frontend/src/App.tsx b/docker/frontend/src/App.tsx index 3f127d5..e4d49d6 100644 --- a/docker/frontend/src/App.tsx +++ b/docker/frontend/src/App.tsx @@ -1050,7 +1050,12 @@ export function Workspace() { if (!engine || !previewBytes || shooting) return; const token = ++renderToken.current; const timer = setTimeout(async () => { - setBusy(t('act.rendering')); + // No spinner for this one, deliberately. The render is the page keeping up + // with the reader — the brush's own spots are already drawn on the layer + // above the photo, so the repair arriving under them a moment later is the + // photo catching up and not a thing being waited for. A pill saying so was + // read as the studio doing something to the frame (an export, a second + // copy), and it stayed up through a HEAL long enough to be noticed. try { // The geometry is the frame's own; only the recipe differs between the // render and the compare copy, so the two can never drift apart. diff --git a/docker/frontend/src/engine/inpaint.ts b/docker/frontend/src/engine/inpaint.ts index ba30c36..0f91220 100644 --- a/docker/frontend/src/engine/inpaint.ts +++ b/docker/frontend/src/engine/inpaint.ts @@ -71,16 +71,32 @@ async function warmsUp(ort: typeof Ort, session: Ort.InferenceSession): Promise< } } +// Whether the browser has a device at all. `requestAdapter` answers for the +// browser and not for this graph — an adapter is no promise the model runs on +// it, the trial below is what settles that — but a *null* answer is a promise +// it does not, and the attempt it saves is not nothing: a `create` that cannot +// have a device still opens a session and tears it down (measured: 0.4s off the +// first repair, and one backend fewer built on the way to the one that lasts). +async function hasDevice(): Promise { + try { + return Boolean(await navigator.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. async function openSession(ort: typeof Ort): Promise { - try { - const gpu = await ort.InferenceSession.create(MODEL_URL, { executionProviders: ['webgpu'] }); - if (await warmsUp(ort, gpu)) return gpu; - await gpu.release(); - } catch { - // Whatever went wrong, the wasm provider is the fallback and not a failure. + if (await hasDevice()) { + try { + const gpu = await ort.InferenceSession.create(MODEL_URL, { executionProviders: ['webgpu'] }); + if (await warmsUp(ort, gpu)) return gpu; + await gpu.release(); + } catch { + // Whatever went wrong, the wasm provider is the fallback and not a failure. + } } return ort.InferenceSession.create(MODEL_URL, { executionProviders: ['wasm'] }); } diff --git a/docker/frontend/src/i18n/en.ts b/docker/frontend/src/i18n/en.ts index 9194638..c527bcc 100644 --- a/docker/frontend/src/i18n/en.ts +++ b/docker/frontend/src/i18n/en.ts @@ -28,9 +28,6 @@ export const en: Dict = { 'act.reset': 'RESET', 'act.export': 'EXPORT', 'act.exporting': 'Exporting…', - // The stage's own re-render behind a brush stroke or a knob: no file is being - // written anywhere, and "Exporting…" over a HEAL was read as exactly that. - 'act.rendering': 'Rendering…', 'act.upscaling': 'Upscaling the photo {done}/{total}…', 'act.cancel': 'Cancel', diff --git a/docker/frontend/src/i18n/vi.ts b/docker/frontend/src/i18n/vi.ts index 23529fb..798302c 100644 --- a/docker/frontend/src/i18n/vi.ts +++ b/docker/frontend/src/i18n/vi.ts @@ -39,7 +39,6 @@ export const vi = { 'act.reset': 'ĐẶT LẠI', 'act.export': 'XUẤT ẢNH', 'act.exporting': 'Đang xuất…', - 'act.rendering': 'Đang dựng ảnh…', 'act.upscaling': 'Đang phóng to ảnh {done}/{total}…', 'act.cancel': 'Huỷ',