onnxruntime/js/web/lib/backend-wasm.ts
Yulong Wang 0627a6cb93
[js/web] fix package export for bundlers (#23257)
### Description
<!-- Describe your changes. -->

This PR tries to fix #22615. (see detailed description in the issue)

A perfect solution would be too difficult to make, because there are a
huge number of combinations of usage scenarios, including combinations
of development framework, bundler, dev/prod mode, and so on.

This PR is using the following approach:
- Introduce a new type of end to end test: export test. This type of
tests are complete web apps that use popular web development frameworks,
and the tests are using puppeteer to run the apps and check if the apps
can run without error.
  - added one nextjs based web app and one vite based web app.
- In the test, perform the following test steps:
  - `npm install` for packages built locally
- `npm run dev` to start dev server and use puppeteer to launch the
browser to test
- `npm run build && npm run start` to test prod build and use puppeteer
to launch the browser to test
- Make changes to ort-web, including:
- special handling on Webpack's behavior of rewriting `import.meta.url`
to a `file://` string
  - revise build definitions
  - fix wasm URL for proxy, if used in a bundled build
2025-01-09 11:01:00 -08:00

95 lines
3.7 KiB
TypeScript

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { Backend, env, InferenceSession, InferenceSessionHandler } from 'onnxruntime-common';
import { initializeOrtEp, initializeWebAssemblyAndOrtRuntime } from './wasm/proxy-wrapper';
import { OnnxruntimeWebAssemblySessionHandler } from './wasm/session-handler-inference';
/**
* This function initializes all flags for WebAssembly.
*
* Those flags are accessible from `ort.env.wasm`. Users are allow to set those flags before the first inference session
* being created, to override default value.
*/
export const initializeFlags = (): void => {
if (typeof env.wasm.initTimeout !== 'number' || env.wasm.initTimeout < 0) {
env.wasm.initTimeout = 0;
}
if (env.wasm.simd === false) {
// eslint-disable-next-line no-console
console.warn(
'Deprecated property "env.wasm.simd" is set to false. ' +
'non-SIMD build is no longer provided, and this setting will be ignored.',
);
}
if (typeof env.wasm.proxy !== 'boolean') {
env.wasm.proxy = false;
}
if (typeof env.wasm.trace !== 'boolean') {
env.wasm.trace = false;
}
if (typeof env.wasm.numThreads !== 'number' || !Number.isInteger(env.wasm.numThreads) || env.wasm.numThreads <= 0) {
// The following logic only applies when `ort.env.wasm.numThreads` is not set by user. We will always honor user's
// setting if it is provided.
// Browser: when crossOriginIsolated is false, SharedArrayBuffer is not available so WebAssembly threads will not
// work. In this case, we will set numThreads to 1.
//
// There is an exception: when the browser is configured to force-enable SharedArrayBuffer (e.g. Chromuim with
// --enable-features=SharedArrayBuffer), it is possible that `self.crossOriginIsolated` is false and
// SharedArrayBuffer is available at the same time. This is usually for testing. In this case, we will still set
// numThreads to 1 here. If we want to enable multi-threading in test, we should set `ort.env.wasm.numThreads` to a
// value greater than 1.
if (typeof self !== 'undefined' && !self.crossOriginIsolated) {
env.wasm.numThreads = 1;
} else {
const numCpuLogicalCores =
typeof navigator === 'undefined' ? require('node:os').cpus().length : navigator.hardwareConcurrency;
env.wasm.numThreads = Math.min(4, Math.ceil((numCpuLogicalCores || 1) / 2));
}
}
};
export class OnnxruntimeWebAssemblyBackend implements Backend {
/**
* This function initializes the WebAssembly backend.
*
* This function will be called only once for each backend name. It will be called the first time when
* `ort.InferenceSession.create()` is called with a registered backend name.
*
* @param backendName - the registered backend name.
*/
async init(backendName: string): Promise<void> {
// populate wasm flags
initializeFlags();
// init wasm
await initializeWebAssemblyAndOrtRuntime();
// performe EP specific initialization
await initializeOrtEp(backendName);
}
createInferenceSessionHandler(
path: string,
options?: InferenceSession.SessionOptions,
): Promise<InferenceSessionHandler>;
createInferenceSessionHandler(
buffer: Uint8Array,
options?: InferenceSession.SessionOptions,
): Promise<InferenceSessionHandler>;
async createInferenceSessionHandler(
pathOrBuffer: string | Uint8Array,
options?: InferenceSession.SessionOptions,
): Promise<InferenceSessionHandler> {
const handler = new OnnxruntimeWebAssemblySessionHandler();
await handler.loadModel(pathOrBuffer, options);
return Promise.resolve(handler);
}
}
export const wasmBackend = new OnnxruntimeWebAssemblyBackend();