onnxruntime/js/web/lib/wasm/proxy-wrapper.ts
Ashrit Shetty df873177eb
Update win-ort-main to tip main 250116 (#23398)
### Description
This PR is to update the win-ort-main branch to the tip main
branch as of 2025-01-16.

### Motivation and Context
This update includes the OpenVino fix for debug builds.

---------

Signed-off-by: Liqun Fu <liqfu@microsoft.com>
Signed-off-by: Liqun Fu <liqun.fu@microsoft.com>
Signed-off-by: Junze Wu <junze.wu@intel.com>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jianhui Dai <jianhui.j.dai@intel.com>
Co-authored-by: Yueqing Zhang <yuz75@Pitt.edu>
Co-authored-by: amancini-N <63410090+amancini-N@users.noreply.github.com>
Co-authored-by: Adrian Lizarraga <adlizarraga@microsoft.com>
Co-authored-by: liqun Fu <liqfu@microsoft.com>
Co-authored-by: Guenther Schmuelling <guschmue@microsoft.com>
Co-authored-by: Yifan Li <109183385+yf711@users.noreply.github.com>
Co-authored-by: yf711 <yifanl@microsoft.com>
Co-authored-by: Wanming Lin <wanming.lin@intel.com>
Co-authored-by: wejoncy <wejoncy@163.com>
Co-authored-by: wejoncy <wejoncy@.com>
Co-authored-by: Scott McKay <skottmckay@gmail.com>
Co-authored-by: Changming Sun <chasun@microsoft.com>
Co-authored-by: Jean-Michaël Celerier <jeanmichael.celerier+github@gmail.com>
Co-authored-by: Dmitry Deshevoy <mityada@gmail.com>
Co-authored-by: xhcao <xinghua.cao@intel.com>
Co-authored-by: Yueqing Zhang <yueqingz@amd.com>
Co-authored-by: Yulong Wang <7679871+fs-eire@users.noreply.github.com>
Co-authored-by: Jiajia Qin <jiajiaqin@microsoft.com>
Co-authored-by: Wu, Junze <junze.wu@intel.com>
Co-authored-by: Jian Chen <cjian@microsoft.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
Co-authored-by: Prathik Rao <prathik.rao@gmail.com>
Co-authored-by: wonchung-microsoft <wonchung@microsoft.com>
Co-authored-by: Vincent Wang <wangwchpku@outlook.com>
Co-authored-by: PARK DongHa <luncliff@gmail.com>
Co-authored-by: Hector Li <hecli@microsoft.com>
Co-authored-by: Sam Webster <13457618+samwebster@users.noreply.github.com>
Co-authored-by: Adrian Lizarraga <adrianlm2@gmail.com>
Co-authored-by: Preetha Veeramalai <preetha.veeramalai@intel.com>
Co-authored-by: jatinwadhwa921 <jatin.wadhwa@intel.com>
Co-authored-by: Satya Kumar Jandhyala <satya.k.jandhyala@gmail.com>
Co-authored-by: Corentin Maravat <101636442+cocotdf@users.noreply.github.com>
Co-authored-by: Xiaoyu <85524621+xiaoyu-work@users.noreply.github.com>
Co-authored-by: Tianlei Wu <tlwu@microsoft.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jie Chen <jie.a.chen@intel.com>
Co-authored-by: Jianhui Dai <jianhui.j.dai@intel.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Edward Chen <18449977+edgchen1@users.noreply.github.com>
Co-authored-by: Baiju Meswani <bmeswani@microsoft.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: Justin Chu <justinchuby@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Ted Themistokleous <107195283+TedThemistokleous@users.noreply.github.com>
Co-authored-by: Jeff Daily <jeff.daily@amd.com>
Co-authored-by: Artur Wojcik <artur.wojcik@outlook.com>
Co-authored-by: Ted Themistokleous <tedthemistokleous@amd.com>
Co-authored-by: Xinya Zhang <Xinya.Zhang@amd.com>
Co-authored-by: ikalinic <ilija.kalinic@amd.com>
Co-authored-by: sstamenk <sstamenk@amd.com>
Co-authored-by: Yi-Hong Lyu <yilyu@microsoft.com>
Co-authored-by: Ti-Tai Wang <titaiwang@microsoft.com>
2025-01-16 15:20:25 -08:00

262 lines
9.1 KiB
TypeScript

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { env, InferenceSession } from 'onnxruntime-common';
import {
OrtWasmMessage,
SerializableInternalBuffer,
SerializableSessionMetadata,
SerializableTensorMetadata,
TensorMetadata,
} from './proxy-messages';
import * as core from './wasm-core-impl';
import { initializeWebAssembly } from './wasm-factory';
import { importProxyWorker, inferWasmPathPrefixFromScriptSrc } from './wasm-utils-import';
const isProxy = (): boolean => !!env.wasm.proxy && typeof document !== 'undefined';
let proxyWorker: Worker | undefined;
let initializing = false;
let initialized = false;
let aborted = false;
let temporaryObjectUrl: string | undefined;
type PromiseCallbacks<T = void> = [resolve: (result: T) => void, reject: (reason: unknown) => void];
let initWasmCallbacks: PromiseCallbacks;
const queuedCallbacks: Map<OrtWasmMessage['type'], Array<PromiseCallbacks<unknown>>> = new Map();
const enqueueCallbacks = (type: OrtWasmMessage['type'], callbacks: PromiseCallbacks<unknown>): void => {
const queue = queuedCallbacks.get(type);
if (queue) {
queue.push(callbacks);
} else {
queuedCallbacks.set(type, [callbacks]);
}
};
const ensureWorker = (): void => {
if (initializing || !initialized || aborted || !proxyWorker) {
throw new Error('worker not ready');
}
};
const onProxyWorkerMessage = (ev: MessageEvent<OrtWasmMessage>): void => {
switch (ev.data.type) {
case 'init-wasm':
initializing = false;
if (ev.data.err) {
aborted = true;
initWasmCallbacks[1](ev.data.err);
} else {
initialized = true;
initWasmCallbacks[0]();
}
if (temporaryObjectUrl) {
URL.revokeObjectURL(temporaryObjectUrl);
temporaryObjectUrl = undefined;
}
break;
case 'init-ep':
case 'copy-from':
case 'create':
case 'release':
case 'run':
case 'end-profiling': {
const callbacks = queuedCallbacks.get(ev.data.type)!;
if (ev.data.err) {
callbacks.shift()![1](ev.data.err);
} else {
callbacks.shift()![0](ev.data.out!);
}
break;
}
default:
}
};
export const initializeWebAssemblyAndOrtRuntime = async (): Promise<void> => {
if (initialized) {
return;
}
if (initializing) {
throw new Error("multiple calls to 'initWasm()' detected.");
}
if (aborted) {
throw new Error("previous call to 'initWasm()' failed.");
}
initializing = true;
if (!BUILD_DEFS.DISABLE_WASM_PROXY && isProxy()) {
return new Promise<void>((resolve, reject) => {
proxyWorker?.terminate();
void importProxyWorker().then(([objectUrl, worker]) => {
try {
proxyWorker = worker;
proxyWorker.onerror = (ev: ErrorEvent) => reject(ev);
proxyWorker.onmessage = onProxyWorkerMessage;
initWasmCallbacks = [resolve, reject];
const message: OrtWasmMessage = { type: 'init-wasm', in: env };
// if the proxy worker is loaded from a blob URL, we need to make sure the path information is not lost.
//
// when `env.wasm.wasmPaths` is not set, we need to pass the path information to the worker.
//
if (!BUILD_DEFS.ENABLE_BUNDLE_WASM_JS && !message.in!.wasm.wasmPaths && objectUrl) {
// for a build not bundled the wasm JS, we need to pass the path prefix to the worker.
// the path prefix will be used to resolve the path to both the wasm JS and the wasm file.
const inferredWasmPathPrefix = inferWasmPathPrefixFromScriptSrc();
if (inferredWasmPathPrefix) {
message.in!.wasm.wasmPaths = inferredWasmPathPrefix;
}
}
if (
BUILD_DEFS.IS_ESM &&
BUILD_DEFS.ENABLE_BUNDLE_WASM_JS &&
!message.in!.wasm.wasmPaths &&
(objectUrl || BUILD_DEFS.ESM_IMPORT_META_URL?.startsWith('file:'))
) {
// for a build bundled the wasm JS, if either of the following conditions is met:
// - the proxy worker is loaded from a blob URL
// - `import.meta.url` is a file URL, it means it is overwriten by the bundler.
//
// in either case, the path information is lost, we need to pass the path of the .wasm file to the worker.
// we need to use the bundler preferred URL format:
// new URL('filename', import.meta.url)
// so that the bundler can handle the file using corresponding loaders.
message.in!.wasm.wasmPaths = {
wasm: !BUILD_DEFS.DISABLE_JSEP
? new URL('ort-wasm-simd-threaded.jsep.wasm', BUILD_DEFS.ESM_IMPORT_META_URL).href
: new URL('ort-wasm-simd-threaded.wasm', BUILD_DEFS.ESM_IMPORT_META_URL).href,
};
}
proxyWorker.postMessage(message);
temporaryObjectUrl = objectUrl;
} catch (e) {
reject(e);
}
}, reject);
});
} else {
try {
await initializeWebAssembly(env.wasm);
await core.initRuntime(env);
initialized = true;
} catch (e) {
aborted = true;
throw e;
} finally {
initializing = false;
}
}
};
export const initializeOrtEp = async (epName: string): Promise<void> => {
if (!BUILD_DEFS.DISABLE_WASM_PROXY && isProxy()) {
ensureWorker();
return new Promise<void>((resolve, reject) => {
enqueueCallbacks('init-ep', [resolve, reject]);
const message: OrtWasmMessage = { type: 'init-ep', in: { epName, env } };
proxyWorker!.postMessage(message);
});
} else {
await core.initEp(env, epName);
}
};
export const copyFromExternalBuffer = async (buffer: Uint8Array): Promise<SerializableInternalBuffer> => {
if (!BUILD_DEFS.DISABLE_WASM_PROXY && isProxy()) {
ensureWorker();
return new Promise<SerializableInternalBuffer>((resolve, reject) => {
enqueueCallbacks('copy-from', [resolve, reject]);
const message: OrtWasmMessage = { type: 'copy-from', in: { buffer } };
proxyWorker!.postMessage(message, [buffer.buffer]);
});
} else {
return core.copyFromExternalBuffer(buffer);
}
};
export const createSession = async (
model: SerializableInternalBuffer | Uint8Array,
options?: InferenceSession.SessionOptions,
): Promise<SerializableSessionMetadata> => {
if (!BUILD_DEFS.DISABLE_WASM_PROXY && isProxy()) {
// check unsupported options
if (options?.preferredOutputLocation) {
throw new Error('session option "preferredOutputLocation" is not supported for proxy.');
}
ensureWorker();
return new Promise<SerializableSessionMetadata>((resolve, reject) => {
enqueueCallbacks('create', [resolve, reject]);
const message: OrtWasmMessage = { type: 'create', in: { model, options: { ...options } } };
const transferable: Transferable[] = [];
if (model instanceof Uint8Array) {
transferable.push(model.buffer);
}
proxyWorker!.postMessage(message, transferable);
});
} else {
return core.createSession(model, options);
}
};
export const releaseSession = async (sessionId: number): Promise<void> => {
if (!BUILD_DEFS.DISABLE_WASM_PROXY && isProxy()) {
ensureWorker();
return new Promise<void>((resolve, reject) => {
enqueueCallbacks('release', [resolve, reject]);
const message: OrtWasmMessage = { type: 'release', in: sessionId };
proxyWorker!.postMessage(message);
});
} else {
core.releaseSession(sessionId);
}
};
export const run = async (
sessionId: number,
inputIndices: number[],
inputs: TensorMetadata[],
outputIndices: number[],
outputs: Array<TensorMetadata | null>,
options: InferenceSession.RunOptions,
): Promise<TensorMetadata[]> => {
if (!BUILD_DEFS.DISABLE_WASM_PROXY && isProxy()) {
// check inputs location
if (inputs.some((t) => t[3] !== 'cpu')) {
throw new Error('input tensor on GPU is not supported for proxy.');
}
// check outputs location
if (outputs.some((t) => t)) {
throw new Error('pre-allocated output tensor is not supported for proxy.');
}
ensureWorker();
return new Promise<SerializableTensorMetadata[]>((resolve, reject) => {
enqueueCallbacks('run', [resolve, reject]);
const serializableInputs = inputs as SerializableTensorMetadata[]; // every input is on CPU.
const message: OrtWasmMessage = {
type: 'run',
in: { sessionId, inputIndices, inputs: serializableInputs, outputIndices, options },
};
proxyWorker!.postMessage(message, core.extractTransferableBuffers(serializableInputs));
});
} else {
return core.run(sessionId, inputIndices, inputs, outputIndices, outputs, options);
}
};
export const endProfiling = async (sessionId: number): Promise<void> => {
if (!BUILD_DEFS.DISABLE_WASM_PROXY && isProxy()) {
ensureWorker();
return new Promise<void>((resolve, reject) => {
enqueueCallbacks('end-profiling', [resolve, reject]);
const message: OrtWasmMessage = { type: 'end-profiling', in: sessionId };
proxyWorker!.postMessage(message);
});
} else {
core.endProfiling(sessionId);
}
};