Add AMDGPU execution provider support - #2194
Conversation
8f3b79d to
a196167
Compare
Register the MorphiZen execution provider (AMD GPU plugin EP, shipped as
onnxruntime_morphizen_ep.dll) so OGA can route models to its allocator
and KV cache stays on GPU instead of falling back to host RAM.
Mirrors the existing RyzenAI integration:
- src/smartptrs.h: add DeviceType::MorphiZenEP.
- src/morphizen_ep/{interface,session_options}.{h,cpp} (new): ported
from src/ryzenai/. SetupProvider filters OrtHardwareDeviceType_GPU;
the EP factory itself restricts to AMD GPU vendor id 0x1002.
- src/generators.cpp: to_string()/GetDeviceInterface()/Shutdown() learn
the new DeviceType.
- src/models/session_options.cpp: dispatch table maps "MorphiZenEP" to
MorphiZenEPExecutionProvider::AppendExecutionProvider.
- src/models/model.cpp: device_type_names[] / device_memory_type_names[]
gain entries; EnsureDeviceOrtInit takes the V2 path for MorphiZenEP
(CreateMemoryInfo_V2 with AMD vendor id + GPU type) because
OrtMemoryInfo::Create only knows ORT-internal device names.
- cmake/global_variables.cmake: add morphizen_ep/* to generator_srcs.
Both past_present_share_buffer=true and =false work with this change;
KV cache routing through the MorphiZen allocator is device-agnostic in
DefaultKeyValueCache.
@microsoft-github-policy-service agree company="AMD" |
Add DeviceType::MorphiZenEP to the devices_supporting_continuous_decoding whitelist in Generator::AppendTokens. Without this entry, multiple AppendTokens calls without an intervening RewindToLength(0) -- as used by teacher-forcing PPL evaluation and any multi-turn chat workload -- throw 'Continuous decoding is not supported on the selected device type'. MorphiZenEP's KV cache lives in UMA-pinned memory (hipHostMallocMapped) which is host-readable and supports the same shared-buffer KV cache semantics as CUDA / WEBGPU / RyzenAI. The fix mirrors the existing entries for those device types.
Add DeviceType::MorphiZenEP to the input-device whitelist so model inputs (input_ids, position_ids, attention_mask, etc.) are allocated via the EP's GPU allocator instead of CPU. This lets ORT alias the buffers directly (zero H2D/D2H copies for inputs and logits); verified end-to-end on Llama-3.1-8B asym with no decode regression and ~22% faster prefill. Also restore an upstream comment in EnsureDeviceOrtInit that was inadvertently reworded.
966d176 to
ca26c92
Compare
2256664 to
1e85eec
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds support for a new ONNX Runtime plugin execution provider (“MorphiZenEP”) by introducing a new device type and wiring provider setup / allocator handling into model/session initialization.
Changes:
- Added
DeviceTypeentry and mappings to/from provider name strings. - Added MorphiZenEP execution provider integration (session options append + EP interface loader/setup).
- Updated allocator initialization logic for MorphiZenEP using ORT’s
CreateMemoryInfo_V2, and hooked build system to compile new sources.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/smartptrs.h | Adds a new DeviceType enum value for MorphiZenEP. |
| src/morphizen_ep/session_options.h | Declares MorphiZenEP AppendExecutionProvider entry point. |
| src/morphizen_ep/session_options.cpp | Implements session option wiring for MorphiZenEP. |
| src/morphizen_ep/interface.h | Declares MorphiZenEP device interface used by the runtime. |
| src/morphizen_ep/interface.cpp | Implements EP registration/loading and provider setup logic for MorphiZenEP. |
| src/models/session_options.cpp | Registers MorphiZenEP in provider dispatch table. |
| src/models/model.cpp | Extends allocator init to support MorphiZenEP allocator via CreateMemoryInfo_V2. |
| src/generators.cpp | Adds MorphiZenEP interface hookup, shutdown, and device type conversions. |
| cmake/global_variables.cmake | Adds MorphiZenEP sources to the build glob. |
| OpenVINO, | ||
| NvTensorRtRtx, | ||
| RyzenAI, | ||
| MorphiZenEP, | ||
| MAX |
There was a problem hiding this comment.
I checked all the references to DeviceType::MorphiZenEP, they are all correct.
| const OrtEpDevice* const* devices = nullptr; | ||
| size_t ndevices = 0; | ||
|
|
||
| Ort::ThrowOnError(Ort::api->GetEpDevices(&GetOrtEnv(), &devices, &ndevices)); | ||
|
|
||
| for (const auto& device : std::span{devices, ndevices}) { |
There was a problem hiding this comment.
It's owned by ORT and will not be released by caller.
…n_ep Address PR review: dlopen(RTLD_NOLOAD) still bumps the refcount of an already-loaded library, so dlclose the returned handle before returning. Also add <string>/<utility>/<vector> and smartptrs.h includes to interface.h instead of relying on transitive includes. Co-authored-by: Cursor <cursoragent@cursor.com>
…#12) Rename the OGA execution provider integration from MorphiZenEP to AMDGPU so it targets the AMD GPU umbrella EP (amdgpu-ep.dll) instead of loading onnxruntime_morphizen_ep.dll directly. The umbrella forwards to the HIP backend, selected via the "profile" provider option. - Rename src/morphizen_ep -> src/amdgpu and DeviceType::MorphiZenEP -> DeviceType::AMDGPU across generators.cpp, model.cpp, the session_options dispatch table, smartptrs.h, and the cmake source glob. - interface.cpp: register the EP as "amdgpu", load amdgpu-ep.dll (libamdgpu-ep.so), env override AMDGPU_EP_PATH. SetupProvider also mirrors provider options into ep.<name>.* session config entries since the umbrella reads them from there. - EnsureDeviceOrtInit: the device-init dummy session reuses the AMDGPU provider options from genai_config so the umbrella can select its backend; no profile is hardcoded. genai_config.json selects it via: "provider_options": [{ "AMDGPU": {"profile": "llm"} }] Co-authored-by: Cursor <cursoragent@cursor.com>
pull/<n>.patch is a multi-commit mbox. `git apply` pre-checks every hunk against the base tree at once, so a PR whose own series adds -> modifies -> renames the same path (microsoft/onnxruntime-genai#2194: src/morphizen_ep/* -> src/amdgpu/*) self-conflicts and the whole build fails. Replay each commit in order with `git am` instead (set a committer identity since the runner has none), which applies the series cleanly. Bump the OGA cache key (-am) so the stale git-apply artifact is not reused. The renamed AMDGPU integration dlopen's libamdgpu-ep.so while the project still builds the EP as libhipgpu.so, so copy it under the expected name in the bundle step (dep-staging + closure verification then cover it). Co-authored-by: Cursor <cursoragent@cursor.com>
| const OrtEpDevice* const* devices = nullptr; | ||
| size_t ndevices = 0; | ||
|
|
||
| Ort::ThrowOnError(Ort::api->GetEpDevices(&GetOrtEnv(), &devices, &ndevices)); |
There was a problem hiding this comment.
Can we use the helper APIs from onnxruntime_inline.h instead of making direct Ort::api calls?
onnxruntime-genai/src/models/onnxruntime_inline.h
Lines 138 to 140 in 6be5300
| } | ||
|
|
||
| for (size_t i = 0; i < config_keys.size(); ++i) { | ||
| Ort::ThrowOnError(Ort::api->AddSessionConfigEntry( |
There was a problem hiding this comment.
Same comment as above for the other direct Ort::api calls in this PR.
onnxruntime-genai/src/models/onnxruntime_inline.h
Lines 678 to 680 in 6be5300
|
|
||
| // Names for the device types used by 'SetProviderSessionOptions' | ||
| static const char* device_type_names[] = {"CPU (Not used, see above)", "cuda", "DML", "WebGPU", "QNN", "OpenVINO (Not used, see above)", "NvTensorRtRtx", "RyzenAI"}; | ||
| static const char* device_type_names[] = {"CPU (Not used, see above)", "cuda", "DML", "WebGPU", "QNN", "OpenVINO (Not used, see above)", "NvTensorRtRtx", "RyzenAI", "AMDGPU"}; |
There was a problem hiding this comment.
Same comment as here regarding simplification. We could create new APIs such as GetProviderName() and GetMemoryInfoName() on DeviceInterface instead of using parallel string arrays.
|
|
||
| // Get the allocator from the OrtSession for the DeviceType (it's called 'AllocatorCreate' but it's really 'AllocatorGet') | ||
| auto name = device_memory_type_names[static_cast<int>(type)]; | ||
| try { |
There was a problem hiding this comment.
I see several conditionals are getting introduced on a per-EP basis for getting an OrtMemoryInfo object. Can we move this per-EP work to the device interface and make it more generic here?
|
Closing this PR in favor of #2165 |
Summary
Register a new
DeviceType::AMDGPUin OGA so models can target the AMD GPU umbrella execution provider (shipped asamdgpu-ep.dll/libamdgpu-ep.so). The umbrella EP forwards to its backend (currently the HIP/MorphiZen backend), selected at runtime via the"profile"provider option. The integration mirrors the existing RyzenAI path: anAMDGPUnamespace +Interface(allocator, dispatch, session-options setup), a dispatch-table entry insession_options.cpp, and aCreateMemoryInfo_V2code path inEnsureDeviceOrtInit(because the plugin EP'sOrtMemoryInfois registered via the V2 API with AMD GPU vendor id 0x1002, not the legacyOrtMemoryInfo::Create). Bothpast_present_share_buffer=trueand=falsework; KV cache routing through the AMD GPU allocator is device-agnostic inDefaultKeyValueCache.Add
DeviceType::AMDGPUto thedevices_supporting_continuous_decodingwhitelist inGenerator::AppendTokens. Without it, multipleAppendTokenscalls without an interveningRewindToLength(0)— as used by teacher-forcing PPL evaluation and multi-turn chat — throw "Continuous decoding is not supported on the selected device type". The AMD GPU EP's KV cache lives in UMA-pinned memory (hipHostMallocMapped, host-readable) and supports the same shared-buffer KV cache semantics as CUDA / WebGPU / RyzenAI, so it qualifies for the same whitelist entry.Allocate AMD GPU model inputs (
input_ids,position_ids,attention_mask, etc.) on device memory via the EP's GPU allocator instead of CPU, so ORT can alias the buffers directly (zero H2D/D2H copies for inputs and logits).Configuration
or
Notes
src/amdgpu/ports fromsrc/ryzenai/with the EP name and vendor filter swapped; behavioural differences are documented inline."amdgpu"and loaded fromamdgpu-ep.dll(libamdgpu-ep.so); the path can be overridden with theAMDGPU_EP_PATHenvironment variable.SetupProvidermirrors provider options intoep.<name>.*session-config entries since the umbrella reads them from there.EnsureDeviceOrtInittakes the V2 OrtMemoryInfo path for AMDGPU because the legacyOrtMemoryInfo::Createonly knows ORT-internal device names ("Cuda", "DML", ...) and would fail with "Specified device is not supported. Try CreateMemoryInfo_V2." for any plugin EP. The device-init dummy session reuses the AMDGPU provider options fromgenai_configso the umbrella can select its backend; no profile is hardcoded.