Repository navigation
[WebGPU] Restore ORT 1.24.4 compatibility with per-Session recording and serialized execution - #33082
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Failed replay cleanup can contaminate the shared legacy recording, and the compatibility path lacks automated CI execution.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Restores WebGPU plugin compatibility with ORT 1.24.4 through a shared serial command-recording path while retaining modern concurrency.
Changes:
- Adds runtime-version-based legacy recording and serialization.
- Updates allocation, transfer, capture, and replay behavior.
- Expands compatibility, ordering, and batching tests and documentation.
| File | Description |
|---|---|
plugin-ep-webgpu/README.md |
Documents compatibility and concurrency contracts. |
plugin-ep-webgpu/MIN_ONNXRUNTIME_VERSION |
Lowers minimum ORT version to 1.24.4. |
onnxruntime/test/providers/webgpu/plugin/test_webgpu_concurrency.cc |
Adds ordering and legacy serialization tests. |
onnxruntime/test/providers/webgpu/plugin/test_webgpu_allocators.cc |
Adds clear-submission and batching tests. |
onnxruntime/core/providers/webgpu/webgpu_provider_factory.cc |
Routes legacy transfers through shared recording. |
onnxruntime/core/providers/webgpu/webgpu_execution_provider.h |
Makes command recording shareable. |
onnxruntime/core/providers/webgpu/webgpu_execution_provider.cc |
Implements legacy Run and replay coordination. |
onnxruntime/core/providers/webgpu/webgpu_context.h |
Stores shared legacy state and run gate. |
onnxruntime/core/providers/webgpu/ep/sync_stream.h |
Declares legacy-mode detection. |
onnxruntime/core/providers/webgpu/ep/sync_stream.cc |
Selects mode from runtime version or override. |
onnxruntime/core/providers/webgpu/ep/README.md |
Documents execution and allocator contracts. |
onnxruntime/core/providers/webgpu/ep/factory.cc |
Configures mode-specific allocators. |
onnxruntime/core/providers/webgpu/ep/api.cc |
Logs compatibility-mode activation. |
onnxruntime/core/providers/webgpu/ep/allocator.h |
Updates allocator contract documentation. |
onnxruntime/core/providers/webgpu/compute_context.cc |
Uses legacy scratch allocation behavior. |
onnxruntime/core/providers/webgpu/buffer_manager.cc |
Clarifies modern allocation behavior. |
onnxruntime/core/providers/webgpu/allocator.h |
Documents submission policies. |
onnxruntime/core/providers/webgpu/allocator.cc |
Implements legacy clear-submission policy. |
cmake/onnxruntime_providers_webgpu.cmake |
Tracks minimum-version file changes. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Address Copilot review feedback on #33082. Discard unfinished shared replay state before the legacy gate is released, covering direct replay and initial replay from OnRunEnd. Add fault-injection tests proving another Session cannot submit abandoned commands. Register a dedicated legacy-safe CTest invocation with the force-legacy environment set before startup, while preserving normal modern coverage. Validated automatic legacy CTest (41 tests, three repeats), modern suite (54 passed, three expected legacy-only skips), and actual ORT 1.24.4 (14 cases). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use a context-owned serial recording on older hosts while preserving Session-owned recordings on modern hosts. Keep external allocation clears submitted and defer legacy Run allocations without per-kernel submissions. Cover raw Dawn readbacks, copy ordering, lifecycle, cancellation, and capture cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address Copilot review feedback on #33082. Discard unfinished shared replay state before the legacy gate is released, covering direct replay and initial replay from OnRunEnd. Add fault-injection tests proving another Session cannot submit abandoned commands. Register a dedicated legacy-safe CTest invocation with the force-legacy environment set before startup, while preserving normal modern coverage. Validated automatic legacy CTest (41 tests, three repeats), modern suite (54 passed, three expected legacy-only skips), and actual ORT 1.24.4 (14 cases). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enable Session-owned recording for ORT 1.28.3+, 1.30.1+, and 1.31+ within the supported release lines. Keep other runtimes on the serial compatibility path, clarify overlapping-Run errors, and cover version boundaries and the legacy override. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use Ort::ThrowOnError after main removed the local helper. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
f57d87c to
693403b
Compare
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Remove failed-replay abandonment and its dedicated fault-injection tests. Retain Run/replay bookkeeping guards and automatic legacy coverage, and document that recovery after replay failure is not guaranteed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6fbaacb-9846-4330-8fb3-2f0b13737d48
Use the default context manager for legacy framework copies and Env allocations while retaining shared recording, per-graph managers, and capture/replay flush boundaries. Add CPU feed/fetch recapture coverage to the existing serial test allowlist. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6fbaacb-9846-4330-8fb3-2f0b13737d48
Add upgrade guidance to the legacy-mode startup log and share Session allocator construction while selecting only the legacy clear-submission policy. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6fbaacb-9846-4330-8fb3-2f0b13737d48
Merge main at 79d3fcf and retain legacy shared recording with the updated context-aware allocator and copy paths. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6fbaacb-9846-4330-8fb3-2f0b13737d48
|
Could we investigate whether legacy hosts can retain Session-owned recording, rather than introducing a separate context-owned recording path? ORT 1.24.4 already forwards supplied streams through the batched This suggests that Session-owned recording, with appropriate legacy allocation behavior and submission boundaries, may support serialized legacy execution and substantially simplify recording ownership, transfer handling, and teardown. Could you trace the remaining single-copy/null-stream paths and identify a concrete case that cannot be ordered correctly with this approach? If shared recording is required, a focused failing regression would clarify why. Otherwise, could we retain Session-owned recording and keep only the necessary old-host compatibility branches? Please validate the candidate on an actual 1.24.4 host, covering dirty-buffer BindInput, CPU feeds/fetches, mixed CPU/GPU execution, and capture/replay. This does not assume concurrency support on older cores; their serialization guarantees remain a separate requirement. |
Replace shared legacy command state with per-Session recordings, flush active Session work before streamless copies, and defer kernel scratch clears explicitly. Rename mode selection to reflect the single-thread calling contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8ca4e5fc-8c08-46bd-82a9-5cff5108efed
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8ca4e5fc-8c08-46bd-82a9-5cff5108efed # Conflicts: # onnxruntime/core/providers/webgpu/webgpu_provider_factory.cc
Use ordinary Tensor construction and the Session allocator submission callback to defer cached-buffer clears during Run. Remove AllocForKernel while retaining per-Session recordings, streamless-copy ordering, and independent Env/concurrent-mode allocations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8ca4e5fc-8c08-46bd-82a9-5cff5108efed
Keep command recording in a unique_ptr. The context only borrows a non-owning pointer during serialized Run/replay for streamless-copy ordering. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8ca4e5fc-8c08-46bd-82a9-5cff5108efed
Keep the caller-serialization contract and borrowed active Session recording without introducing a persistent Run gate. Replace the overlap-rejection test with a regression proving normal Runs on both Sessions remain possible after a rejected stream override. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8ca4e5fc-8c08-46bd-82a9-5cff5108efed
Move host-version and override policy out of sync_stream into runtime_compatibility. Use serialized-execution naming across call sites, borrowed recording state, tests, and documentation without changing execution behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6fbaacb-9846-4330-8fb3-2f0b13737d48
Merge microsoft/onnxruntime main at e19a9ae, retaining Session-owned recording and the extracted serialized runtime policy. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6fbaacb-9846-4330-8fb3-2f0b13737d48
|
Jiajia Qin (@qjia7) Thanks for the detailed suggestion in #33082 (comment). Your distinction between batched The implementation now in this PR (20ff4e2) keeps each recording exclusively owned by its Session. Serialized-mode Session allocations submit cached-buffer clears outside Run and defer them during Run; scratch uses the same ordinary allocator without One submission boundary is still needed: on 1.24.4, mixed-direction fetches can fall back to the single-tensor wrapper and lose their streams. In a concrete local repro, two independent branches produce a GPU Neg output fetched to a preallocated CPU target and a CPU Neg output fetched to a preallocated GPU target. The GPU input is also preallocated/uploaded before Run. The Session-owned prototype without the pre-copy flush returned 0 instead of -1 on the first iteration. The current implementation passes both output orderings by submitting pending active-Session work before the independent streamless copy. The context holds only a non-owning pointer for that boundary. This demonstrates a need for ordering coordination, not shared recording ownership. I reran the final merged plugin on an actual ORT 1.24.4 host: all nine local-harness scenarios passed, 20 iterations each, including dirty-buffer BindInput, CPU feeds/fetches, mixed-direction copies, mixed CPU/GPU execution, and scratch reuse. Profiles confirm the intended provider placement. Capture coverage on 1.24.4 is specifically initial capture plus the internal replay; later Runs are ordinary execution because that host lacks the plugin capture/replay callbacks. Repeated host-driven replay is covered separately by the current-host tests, where all 41 serialized and 57 concurrent-mode tests passed. The standalone old-host harness is local, not a checked-in CI test. The strict caller-serialization requirement remains. The context-wide overlap gate has been removed to avoid stale-gate failures when the host skips |
CaptureBegin already submits pending Session work and refreshes the graph and default managers. Keep streamless-copy and Run-end submission boundaries unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6fbaacb-9846-4330-8fb3-2f0b13737d48
Jiajia Qin (qjia7)
left a comment
There was a problem hiding this comment.
LGTM after all CI bots are green.
BTW, should we also cherry-pick #32902 to the 1.28 release branch so concurrent Runs using captured graphs are properly serialized? Since this PR enables concurrent mode on 1.28.3, should we keep that version in serialized mode until a patched release includes the fix?
### Description <!-- Describe your changes. --> This cherry-picks the following commits for the release: | Commit ID | PR Number | Commit Title | |-----------|-----------|-------------| | ac44e16 | #33208 | Make shared WebGPU data transfers stateless | | 6e0f154 | #33082 | [WebGPU] Restore ORT 1.24.4 compatibility with per-Session recording and serialized execution | ### Motivation and Context <!-- - Why is this change required? What problem does it solve? - If it fixes an open issue, please link to the issue here. --> WebGPU EP 0.5.0 release preparation. --------- Co-authored-by: Mustapha Jaber <mustjab@users.noreply.github.com> Co-authored-by: Xiaofei Han <xiaofeihan@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6fbaacb-9846-4330-8fb3-2f0b13737d48 Copilot-Session: 8ca4e5fc-8c08-46bd-82a9-5cff5108efed


Description
Restore WebGPU plugin compatibility starting with ORT 1.24.4, while retaining Session-owned command recording in both execution modes. The additional real-host matrix below found an unresolved ORT 1.26.0 host-side rejection; this is not a claim that every intervening release currently works.
unique_ptr; there is no context-owned shared legacy recording.UseSerializedExecutionMode()andShouldUseSerializedExecutionMode(), now isolated inep/runtime_compatibility.h/.ccrather than the stream implementation.Allocand matchingAllocOnStreamuse!IsRunActive()to submit cached-buffer clears outside Run and defer them during Run. Kernel scratch uses ordinary Tensor allocation through the same allocator, without anAllocForKernelAPI or the unavailable old-hostKernelContext_GetSyncStream.SerialRunsAfterRejectedStreamOverridecovers a rejected stream override followed by successful serial Runs on both the affected Session and another Session.Set
ORT_WEBGPU_EP_FORCE_LEGACY=1before plugin registration to force serialized compatibility. The existing override andonnxruntime_webgpu_legacy_testCTest name are retained for compatibility; neither implies shared recording. The override cannot enable concurrency on an unsupported host.Serialized mode requires callers to serialize all WebGPU operations on the same device, including Session creation/destruction, Run, allocation, I/O binding, and Env transfers. Use sequential graph execution. This is not a fixed-CPU-thread requirement, and the plugin neither detects nor serializes overlapping operations. Callback reentry remains unsupported.
Input/output copy ordering
ORT 1.24.4 forwards supplied streams through batched
CopyTensors; its separate single-tensor wrapper drops the stream.BindInputuses a single-input copy, and mixed source/destination device pairs can make a batch fall back to individual copies.BindInputoutside Run, CPU to GPUAllocOnStream. A subsequent streamless upload is ordered after it on the device queue; no active Run pointer is needed.OnRunEnd, which occurs after output copies.These are submission boundaries, not per-kernel CPU waits. Readback retains its existing completion wait. Compared with a shared recording, a streamless copy with pending Session work can require an additional queue submission; performance equivalence has not been established.
Motivation and Context
Allow independently distributed WebGPU plugins to support older applications without requiring the host-side single-copy stream-forwarding fix in #32666. Builds on #32874.
Following review, the implementation now keeps Session-owned recording rather than introducing a second ownership model for old hosts. A real ORT 1.24.4 mixed-output repro demonstrated the missing submission boundary: an independent GPU Neg branch fetched to CPU returned 0 instead of -1 when the Session's pending computation was not submitted before a streamless readback. The other independent CPU branch was fetched to GPU, triggering mixed-direction single-copy fallback. Both targets and the GPU input were preallocated. Adding the boundary makes both output orderings pass; this shows the need for ordering coordination, not shared recording ownership.
The alternative developed in #33228 has been incorporated here, and that duplicate PR is closed. This revision includes upstream
mainate19a9ae831.Scope: Removing the context-wide gate avoids permanently blocking other Sessions when a host error skips
OnRunEnd. It does not guarantee recovery of all unfinished Run/capture state. Clarifying the host'sOnRunStart/OnRunEnderror-path contract and complete failed-replay recovery remain separate follow-up work.Validation
Validated commit
20ff4e2f63on Windows x64 Release with the shared WebGPU plugin and EP API adapters. Current-host tests use ORT 1.32.0.W:was a temporary mapping to this worktree and was removed afterward.Build: passed.
Serialized compatibility: 41 executed tests passed, no failures or skips. CTest sets
ORT_WEBGPU_EP_FORCE_LEGACY=1before loading the plugin and selects serial-safe cases.Concurrent-mode selection: 57 executed tests passed, no failures or skips. Two pre-existing disabled diagnostic tests were not enabled in either selection.
Coverage includes version boundaries, allocation-clear submission, scratch batching, serial and concurrent capture/replay, mixed feeds/fetches, and serial Runs after a rejected stream override.
Actual ORT 1.24.4: all 9 local-harness scenarios passed, 20 iterations each. The standalone API-24 C++ probe loaded the official 1.24.4 DLL and the plugin rebuilt from this commit; profiles confirmed CPU/WebGPU node placement. Cases cover CPU I/O, dirty-buffer BindInput, mixed-direction inputs and outputs in both orders, GPU-to-CPU-to-GPU graph execution, scratch reuse, and capture-enabled execution.
The following invocation used the existing local
recording-ownership-probedirectory ($probe); this harness is not part of the repository or CI:Capture limitation: the actual 1.24.4 run exercised initial capture and its internal replay. Its subsequent Runs did not exercise host-driven replay, because that host lacks the plugin capture/replay callbacks. Repeated host-driven replay was covered by the current-host tests above.
Other operating systems and performance benchmarks were not tested in the above
20ff4e2f63validation.Additional local validation at
62e3504c2eRemoved only the redundant pre-capture
OnRunStartflush.CaptureBeginstill flushes before entering capture; streamless-copy, Run-end, and standalone replay submission boundaries are unchanged. The plugin rebuilt successfully, and the three focused serialized capture tests passed:A separate local matrix used one unchanged plugin DLL with official Windows x64 cores 1.24.4, 1.25.1, 1.26.0, 1.27.1, 1.28.3, 1.29.1, and 1.30.0, plus the local 1.32.0 Release core. Loaded module paths, actual core versions, and plugin hashes were checked.
OrtEp::ort_version_supported=32because its kernel-creation check requires a value no greater than 26. This host restriction remains unresolved; forcing serialized mode does not address it.Ifnode. Tested cores from 1.26 onward reject capture at Session initialization. On 1.24.4 and 1.25.1, initialization succeeds but generated tokens differ after rewinding and repeating the same prompt. This also reproduces without profiling and with the pre-cleanup20ff4e2f63plugin, so it was not introduced by removing the redundant flush. No capture-ON throughput is reported for this model.The matrix scripts and model are session-local, not checked into the repository or run by CI.
$matrixbelow refers to the isolatedphi4-core-matrixartifact directory: