revert(studio): the repair is drawn once again, and the model's own answer is the only one the stage shows

The two rounds were meant to answer the stroke sooner, but a preview cannot be
half a repair: the blurred fill is what a machine without the runtime gets, and
with it on the stage a spot of dirt came back smeared rather than gone — worst of
all on a run of strokes, where each new one cancels the model's round that was
still on its way and leaves the blur standing longest. The repair that works is
the one that waits for the model, and the wait is worth it.

The worker stays available but is switched off with the rest: this reverts the
runtime's proxy along with the second round, so the stage is exactly what it was
before, lag and all.

Reverts 5489114.
This commit is contained in:
2026-10-12 22:05:45 +07:00
parent a4b9c41247
commit 550e688b3c
3 changed files with 1 additions and 44 deletions
-24
View File
@@ -1032,30 +1032,6 @@ export function Workspace() {
// render and the compare copy, so the two can never drift apart.
const frame = cropEditing ? 'none' : frameId;
const options = stageOptions;
// A repair in hand is drawn twice, and the first one is the cheap half:
// the blurred fill lands in a fraction of the model's fixed seconds and
// is a repair in its own right (it is what a machine without the runtime
// gets), so the stroke is answered at once and the model's version of
// the same mask replaces it when it arrives. Everything the app files or
// exports still waits for the model.
if (shownRecipe.adjustments.heal?.length) {
const quick = await renderPhoto({
source: previewBytes,
recipe: shownRecipe,
frameId: frame,
useGeotag,
gpsInfo,
assets: engine.assets,
options: { ...options, healModel: false },
});
if (token !== renderToken.current) return;
if (quick) {
setPreviewUrl((prev) => {
if (prev) URL.revokeObjectURL(prev);
return URL.createObjectURL(new Blob([quick.bytes as BlobPart], { type: 'image/jpeg' }));
});
}
}
const out = await renderPhoto({
source: previewBytes,
recipe: shownRecipe,
+1 -13
View File
@@ -120,14 +120,6 @@ export interface RenderOptions {
// band appended below it, mirroring the landing page's `.lp-shot` +
// `.lp-frame-meta`. Left off the live preview, which must not show it.
caption?: { tag: string; title: string; meta: string } | null;
// The preview's own rush: draw the frame with the blurred fill alone, and the
// model's repair of the same mask arrives in the next render. The graph is a
// fixed cost whatever it is handed — through the runtime the app loads, 64x64
// and 512x512 both measure the same two seconds — so the only thing that makes
// a stroke feel answered is showing the half of HEAL that is instant.
// Everything filed or exported leaves this on: a faster answer is worth
// something to an eye waiting on the brush, and to nothing else.
healModel?: boolean;
}
export interface RenderInput {
@@ -1470,11 +1462,7 @@ export async function renderPhoto(input: RenderInput): Promise<RenderResult | nu
const srcBottom = Math.min(boxBottom, Math.ceil(my1 + margin));
const srcW = srcRight - srcLeft;
const srcH = srcBottom - srcTop;
// ...and the preview may pass on the model entirely, keeping the blurred
// fill above (see RenderOptions.healModel): the stroke is answered while
// the model is still thinking, and the answer it is owed arrives in the
// render after.
if (options?.healModel !== false && srcW >= 8 && srcH >= 8) {
if (srcW >= 8 && srcH >= 8) {
const { w: mw, h: mh } = inpaintCropSize(srcW, srcH);
const cropSurface = own(createSurface(mw, mh));
if (cropSurface) {
-7
View File
@@ -99,13 +99,6 @@ function load(): Promise<Loaded> {
ort.env.wasm.numThreads = self.crossOriginIsolated
? Math.min(4, navigator.hardwareConcurrency || 1)
: 1;
// ...and the one thread there is runs in a worker of its own rather than
// on the page's own. The graph is seconds of solid arithmetic at this size,
// and a second of it on the main thread is a second in which the pointer
// does not move: the brush lagged behind the hand exactly as long as the
// repair took. The worker loads the same file WASM_DIR serves. WebGPU is
// off-thread on its own and ignores this.
ort.env.wasm.proxy = true;
ort.env.webgpu.powerPreference = 'high-performance';
const session = await openSession(ort);
return { ort, session };