-
Notifications
You must be signed in to change notification settings - Fork 4.2k
[onnxruntime-web] Plans to deprecate and remove WebGL and JSEP #29716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Edward Chen (edgchen1)
merged 10 commits into
main
from
edgchen1/ort_web_backend_deprecation_plans
Aug 4, 2026
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
524d59a
Add design docs for onnxruntime-web backend consolidation
edgchen1 fc62ff6
Address design-doc review feedback
edgchen1 288864a
Tighten onnxruntime-web backend deprecation design docs
edgchen1 3788336
Reconcile /all sequencing and refine onnxruntime-web backend deprecat…
edgchen1 e3c889f
Document JSEP deprecation-window extension in Phase 2
edgchen1 a358c0a
Address PR 29716 review feedback on onnxruntime-web deprecation docs
edgchen1 fe0516f
Simplify onnxruntime-web backend deprecation design docs
edgchen1 89f4939
Address PR review comments on onnxruntime-web backend deprecation des…
edgchen1 c8c83da
Clarify WebNN framing in onnxruntime-web deprecation design docs
edgchen1 3a1ac44
more feedback
edgchen1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
206 changes: 206 additions & 0 deletions
206
docs/design/onnxruntime_web_jsep_to_webgpu_ep_migration.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| # Design: Migrate onnxruntime-web from JSEP to the native WebGPU EP | ||
|
|
||
| **Scope:** `onnxruntime-web` JavaScript/TypeScript package — WebGPU backend; WebNN initialization glue | ||
|
|
||
| **Related work:** [Remove the WebGL (onnxjs) backend from onnxruntime-web](onnxruntime_web_remove_webgl_backend.md) | ||
| — independent, but shares the `onnxruntime-web/all` bundle and the deprecation-warning utility. | ||
|
|
||
| --- | ||
|
|
||
| ## 1. Summary | ||
|
|
||
| `onnxruntime-web` implements WebGPU compute two ways: | ||
|
|
||
| - **JSEP** — the WebGPU compute path implemented in TypeScript over an Asyncify-compiled WASM core. Powers the | ||
| default (`.`) and `./all` bundles. | ||
| - **Native WebGPU EP** — the C++ WebGPU execution provider compiled to WASM. Powers the `./webgpu` and `./jspi` | ||
| bundles. | ||
|
|
||
| This document proposes removing JSEP and standardizing on the native WebGPU EP. The default bundle keeps the | ||
| `webgpu` backend key and swaps implementation at build time, so the change is transparent for existing code. WebNN | ||
| is unaffected: it is already the native C++ WebNN EP in both builds (both link `--use_webnn`; `session-options.ts` | ||
| selects the `WEBNN` EP either way). Removing JSEP only drops the shared JS init glue (`jsepInit('webnn', …)` → | ||
| `webnnInit(…)`, same `WebNNBackend`), not the WebNN EP itself. | ||
|
|
||
| - **Phase 1 (this release):** flip the default (`.`) and `./all` bundles to the native WebGPU EP, add a temporary | ||
| `onnxruntime-web/jsep` escape-hatch export for one release, and ship deprecation warnings + docs. | ||
| - **Phase 2 (subsequent release):** delete JSEP and remove the temporary `/jsep` export and build flags. | ||
|
|
||
| --- | ||
|
|
||
| ## 2. Motivation | ||
|
|
||
| - **Remove duplicate maintenance.** JSEP and the native WebGPU EP implement the same operators twice (TypeScript | ||
| and C++). The native WebGPU EP is the strategic direction and receives new op work. | ||
| - **Consistent runtime semantics.** The browser build shares kernel behavior with the rest of ONNX Runtime | ||
| instead of maintaining a parallel TS implementation. | ||
| - **Simpler build matrix.** Removing JSEP deletes the temporary `USE_WEBGPU_EP` / `DISABLE_JSEP` build plumbing. | ||
|
|
||
| --- | ||
|
|
||
| ## 3. Goals and non-goals | ||
|
|
||
| ### Goals | ||
|
|
||
| - Make the native WebGPU EP the default WebGPU backend, transparently for existing consumers (same import, | ||
| `webgpu` key, and public API). | ||
| - Give JSEP consumers a low-effort migration path plus a one-release safety net. | ||
| - Remove the JSEP TypeScript compute path and its build variants. | ||
|
|
||
| ### Non-goals | ||
|
|
||
| - **WebGL removal** — tracked in [onnxruntime_web_remove_webgl_backend.md](onnxruntime_web_remove_webgl_backend.md). | ||
| - No change to the Node binding, React-Native package, or ORT training-web. | ||
| - No new WebGPU features — this is a consolidation. | ||
|
|
||
| --- | ||
|
|
||
| ## 4. Background: backend selection | ||
|
|
||
| Whether a bundle uses JSEP or the native WebGPU EP is a build-time choice (distinct from the runtime | ||
| `executionProviders` selection), gated by the `BUILD_DEFS` flags `DISABLE_JSEP` and `DISABLE_WEBGPU` (native WebGPU | ||
| EP). The pivot is the temporary `USE_WEBGPU_EP` flag in `js/web/script/build.ts` (default `false`), | ||
| which sets `DISABLE_JSEP = !!USE_WEBGPU_EP` and `DISABLE_WEBGPU = !USE_WEBGPU_EP`. Both `USE_WEBGPU_EP` and | ||
| `USE_JSPI` are annotated in the source as temporary. | ||
|
|
||
| ### 4.1 Current bundle / export map (`js/web/package.json`) | ||
|
|
||
| | Export | WebGPU impl | Async bridge | WASM binary suffix | | ||
| |---|---|---|---| | ||
| | `.` (default) | JSEP | Asyncify | `.jsep.wasm` | | ||
| | `./all` | JSEP | Asyncify | `.jsep.wasm` | | ||
| | `./webgpu` | native WebGPU EP | Asyncify | `.asyncify.wasm` | | ||
| | `./jspi` | native WebGPU EP | JSPI | `.jspi.wasm` | | ||
| | `./wasm` | — (CPU only) | — | `.wasm` | | ||
|
|
||
| --- | ||
|
|
||
| ## 5. Approach | ||
|
|
||
| - **Transparent default swap.** Both JSEP and the native WebGPU EP register under the `webgpu` key, so flipping | ||
| the default bundle to the native WebGPU EP requires no consumer source changes. | ||
| - **Escape hatch.** Phase 1 adds a temporary `onnxruntime-web/jsep` export (built `USE_WEBGPU_EP=false`) that pins | ||
| JSEP for one release. It is deprecated and warns once, doubling as a parity-bug funnel. | ||
| - **`/all` bundle.** `/all` bundles the WebGPU/WebNN backend and WebGL — two independent things changed by two | ||
| efforts (this one flips WebGPU to native; the WebGL effort drops WebGL). It is kept to avoid breaking | ||
| imports. Once both land, `/all` becomes a real alias of the webgpu/default artifact (`.asyncify.wasm`). The two | ||
| efforts may land in either order; whichever lands second performs the repoint (§9, WebGL doc §8). | ||
|
|
||
| --- | ||
|
|
||
| ## 6. int64 handling | ||
|
|
||
| The native WebGPU EP reads int64 support from `ep.webgpuexecutionprovider.enableInt64` (`webgpu_provider_factory.cc`), | ||
| default `false`. It is reachable on `/webgpu` today via the untyped `extra` map | ||
| (`extra: { 'ep.webgpuexecutionprovider.enableInt64': '1' }`). | ||
|
|
||
| With int64 **off** (the default), the native WebGPU EP matches JSEP exactly: int64 arithmetic runs on CPU/WASM with full | ||
| `int64_t` precision, while int64 indices (`Gather` / `GatherElements` / `MaxPool`) run on the GPU with i32 | ||
| truncation. `enableInt64 = 1` is an opt-in that additionally runs int64 arithmetic on the GPU — faster, but | ||
| i32-truncated and therefore lossy for genuine `> 2³¹` values. `transformers.js` already runs the native WebGPU EP | ||
| with int64 off at scale. | ||
|
|
||
| **Decision:** keep int64 off by default. `enableInt64 = 1` is an opt-in tradeoff, not needed for JSEP parity. | ||
| Optional follow-up: a typed `enableInt64?: boolean` option. | ||
|
|
||
| Graph capture forces int64 on (`enable_int64_{enable_graph_capture || enable_int64}` in | ||
| `webgpu_execution_provider.cc`) to keep the captured region all-GPU. So `enableGraphCapture = true` silently moves | ||
| int64 arithmetic to the GPU and truncates genuine `> 2³¹` values — an exception to default parity, flagged in the | ||
| migration guide (§11). | ||
|
|
||
| --- | ||
|
|
||
| ## 7. Items to validate before the flip | ||
|
|
||
| Parity checks to close before Phase 1. Items 1–3 need a real browser + GPU/WebNN run; item 4 is a known wiring gap | ||
| to fix. | ||
|
|
||
| 1. **Proxy-worker (`wasm.proxy = true`).** `./webgpu` already ships this exact path (native WebGPU EP + Asyncify + proxy | ||
| over the EP-agnostic `proxy-wrapper.ts`), so this is a coverage check, not new wiring. CI today runs `--wasm.proxy` | ||
| only with `-b=wasm` (CPU) on the JSEP build — never `-b=webgpu`, and the `--webgpu-ep` leg runs no proxy. Confirm a | ||
| real proxy-mode run on a GPU: native WebGPU EP device init and threading inside a Worker. | ||
| 2. **IO-binding.** The `'gpu-buffer'` / `'ml-tensor'` paths in `wasm-core-impl.ts` have symmetric native/JSEP | ||
| hooks. CI today runs the `--io-binding=gpu-tensor` / `gpu-location` legs on the JSEP build only; the `--webgpu-ep` | ||
| leg passes no `--io-binding`. Confirm the native path delivers true zero-copy handoff and matching dispose/lifetime | ||
| semantics on a GPU. | ||
| 3. **WebNN.** WebNN is already the native C++ WebNN EP in both builds (`session-options.ts` selects `WEBNN` | ||
| either way); the flip only swaps the JS init bridge (`jsepInit('webnn', …)` → `webnnInit(…)`, same | ||
| `WebNNBackend`). Confirm the native `webnnInit` path behaves the same in a `navigator.ml`-capable environment. | ||
| 4. **Global `env.webgpu.*` settings (wiring gap).** `wasm-core-impl.ts` requests the adapter on the JS side but | ||
| calls `webgpuInit()` without forwarding it to the native WebGPU EP, and `startProfiling()` is a TODO on the native | ||
| path — so `adapter` / `powerPreference` / `forceFallbackAdapter` / `profiling` are silently dropped. Wire these | ||
| into the native path (or document the change) before the flip. | ||
|
|
||
| Per-session typed options are already a superset of JSEP's, confirmed by source audit. | ||
|
|
||
| --- | ||
|
|
||
| ## 8. Phase 1 — Flip + deprecate (this release) | ||
|
|
||
| 1. **Flip the default.** Build `.` and `./all` with the native WebGPU EP (`USE_WEBGPU_EP=true`). The `webgpu` key and | ||
| public API are unchanged. `/all` keeps WebGL until the WebGL effort removes it. | ||
| 2. **Escape hatch.** Add the temporary, deprecated `onnxruntime-web/jsep` export (`USE_WEBGPU_EP=false`). | ||
| 3. **Warn once.** The `/jsep` build warns once (respecting `env.logLevel`) via a shared deprecation-warning | ||
| utility, also used by the WebGL effort; the native default emits nothing. | ||
| 4. **Publish the tracking issue** ahead of the release as the early-warning channel. | ||
| 5. **Docs.** Deprecation banners + migration guidance in `js/web/README` (hand-authored) and a release-notes entry | ||
| covering the `.jsep.wasm` → `.asyncify.wasm` filename change, `/jsep` pinning, and the int64 `extra` opt-in. | ||
| 6. **Retarget the operator-doc generator.** `generate-webgpu-operator-md.ts` parses the JSEP registrations | ||
| (`js_execution_provider.cc`, `js_contrib_kernels.cc`), so post-flip `webgpu-operators.md` describes the wrong | ||
| EP; point it at the native WebGPU EP registrations (those JSEP sources are removed in Phase 2). | ||
|
|
||
| **Release gate.** The flip is gated on both: (a) a **blocking** CI job running the native WebGPU EP — the current | ||
| native-WebGPU-EP legs are advisory (`continue-on-error` in `windows-web-ci-workflow.yml`, `continueOnError` in | ||
| `win-web-ci.yml`) and must be promoted to blocking. The default `.` bundle and `./webgpu` are the same build, so a | ||
| green `/webgpu` run covers the flip only if it exercises proxy/IO-binding/WebNN — which the current op-parity leg | ||
| does not; and (b) the §7 items resolved with targeted coverage (op-parity tests don't exercise those paths). | ||
|
|
||
| --- | ||
|
|
||
| ## 9. Phase 2 — Removal (subsequent release) | ||
|
|
||
| Timed one release after Phase 1, keeping the `/jsep` hatch available for exactly one release. Extend only if | ||
| native-WebGPU-EP parity regressions surface via real `/jsep` usage. | ||
|
|
||
| 1. Drop JSEP WASM artifacts; update `build.ts` / `package.json`; remove the `USE_WEBGPU_EP` flag. Repoint `/all` | ||
| to the webgpu/default artifact — only once WebGL has also been dropped (WebGL doc §8); otherwise `/all` stays a | ||
| distinct bundle until then. | ||
| 2. Delete `BUILD_DEFS.DISABLE_JSEP` and the code it gates; simplify `index.ts`. | ||
| 3. Relocate WebNN out of `jsep/` (`backend-webnn.ts`, `webnn/`) to a neutral path. | ||
| 4. Remove `pre-jsep.js` glue; confirm `post-webgpu.js` / `post-webnn.js` cover initialization. | ||
| 5. Remove the `onnxruntime-web/jsep` export. A lingering `import 'onnxruntime-web/jsep'` then fails with the | ||
| native bundler error — an acceptable build-time failure on this temporary surface. The default `.` import is | ||
| unaffected. | ||
|
|
||
| --- | ||
|
|
||
| ## 10. Risks and mitigations | ||
|
|
||
| | Risk | Likelihood | Mitigation | | ||
| |---|---|---| | ||
| | Undiscovered native-WebGPU-EP parity gap vs. JSEP | Medium | One-release `/jsep` escape hatch + warn-once funnel; differential tests | | ||
| | int64 behavior change vs. JSEP | Low | None by default (native-off matches JSEP); `enableInt64 = 1` is an opt-in tradeoff | | ||
| | Proxy / IO-binding / WebNN unexercised in the native-WebGPU build in CI | Unknown | Validate before flip (§7) | | ||
| | Global `env.webgpu.*` settings dropped on native | Medium | Wire into the native path or document — release gate (§7, §8) | | ||
| | WASM filename change breaks `wasmPaths` | Medium | Document `.jsep.wasm` → `.asyncify.wasm` | | ||
| | No telemetry on JSEP adoption | Medium | Warn-once funnel + one-release escape hatch | | ||
|
|
||
| --- | ||
|
|
||
| ## 11. Migration guide | ||
|
|
||
| - **Default import (`onnxruntime-web`) and `onnxruntime-web/all`:** no source change if you use default, | ||
| package-managed asset resolution; the `webgpu` backend keeps working and converges to the native WebGPU EP. | ||
| Consumers that pin WASM artifacts must still update the path (see `wasmPaths` below). | ||
| - **Lower-overhead build (`onnxruntime-web/jspi`):** on JSPI-capable browsers, prefer `./jspi` — the same native | ||
| WebGPU EP with a smaller WASM binary and lower per-call overhead than Asyncify (the universal fallback). | ||
| - **Need JSEP for one more release:** import `onnxruntime-web/jsep` (temporary, deprecated). File an issue if the | ||
| native WebGPU EP does not work for your model. | ||
| - **int64-heavy models:** no change needed — the default matches JSEP. Exceptions that move int64 arithmetic to the | ||
| GPU (lossy for genuine `> 2³¹` values): `extra: { 'ep.webgpuexecutionprovider.enableInt64': '1' }`, and | ||
| `enableGraphCapture = true` (forces int64 on; see §6). | ||
| - **`wasmPaths` / pinned artifacts:** the backing artifact changes (`.jsep.wasm` → `.asyncify.wasm`). Update | ||
| object-form `env.wasm.wasmPaths`, direct artifact imports, preload/CSP rules, and any copied assets. | ||
|
|
||
| > A pinned tracking issue (link TBD) is the authoritative, continuously-updated migration guidance. Warnings, | ||
| > docs, and CHANGELOG entries link to it. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.