Skip to content

Commit 35cb57b

Browse files
committed
WinML - Source Changes
1 parent d9dbfb9 commit 35cb57b

6 files changed

Lines changed: 161 additions & 6 deletions

File tree

src/config.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,12 @@ struct Model_Element : JSON::Element {
706706
v_.decoder_start_token_id = static_cast<int>(JSON::Get<double>(value));
707707
} else if (name == "sep_token_id") {
708708
v_.sep_token_id = static_cast<int>(JSON::Get<double>(value));
709+
} else if (name == "hardware_device_type") {
710+
v_.hardware_device_type = JSON::Get<std::string_view>(value);
711+
} else if (name == "hardware_device_id") {
712+
v_.hardware_device_id = static_cast<uint32_t>(JSON::Get<double>(value));
713+
} else if (name == "hardware_vendor_id") {
714+
v_.hardware_vendor_id = static_cast<uint32_t>(JSON::Get<double>(value));
709715
} else {
710716
throw JSON::unknown_value_error{};
711717
}

src/config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@ struct Config {
247247

248248
} decoder;
249249

250+
// EP device filters
251+
std::optional<std::string> hardware_device_type; // CPU, GPU, NPU
252+
std::optional<uint32_t> hardware_device_id;
253+
std::optional<uint32_t> hardware_vendor_id;
254+
250255
} model;
251256

252257
struct Search {

src/models/model.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,12 +589,141 @@ DeviceInterface* SetProviderSessionOptions(OrtSessionOptions& session_options,
589589
p_device = GetDeviceInterface(DeviceType::NvTensorRtRtx);
590590
}
591591

592+
#if USE_WINML
593+
// Get model device config
594+
std::optional<uint32_t> config_device_id = config.model.hardware_device_id;
595+
std::optional<uint32_t> config_vendor_id = config.model.hardware_vendor_id;
596+
std::optional<std::string> config_device_type = config.model.hardware_device_type;
597+
// for OpenVINO, use "device_type" in provider_options exclusively if it's provided
598+
std::optional<std::string> config_ov_device_type = std::nullopt;
599+
if (provider_options.name == "OpenVINO") {
600+
for (auto& option : provider_options.options) {
601+
if (option.first == "device_type") {
602+
config_ov_device_type = option.second;
603+
}
604+
}
605+
if (config_ov_device_type.has_value()) {
606+
config_device_id = std::nullopt;
607+
config_vendor_id = std::nullopt;
608+
config_device_type = std::nullopt;
609+
} else if (!(config_device_id.has_value() || config_vendor_id.has_value() || config_device_type.has_value())) {
610+
config_ov_device_type = "CPU";
611+
}
612+
}
613+
std::optional<OrtHardwareDeviceType> config_device_type_enum;
614+
if (config_device_type.has_value()) {
615+
if (*config_device_type == "CPU") {
616+
config_device_type_enum = OrtHardwareDeviceType_CPU;
617+
} else if (*config_device_type == "GPU") {
618+
config_device_type_enum = OrtHardwareDeviceType_GPU;
619+
} else if (*config_device_type == "NPU") {
620+
config_device_type_enum = OrtHardwareDeviceType_NPU;
621+
} else {
622+
throw std::runtime_error("Unsupported hardware device type: " + *config_device_type);
623+
}
624+
}
625+
626+
// Match EP device with EP name in provider options and model device config
627+
// include\onnxruntime\core\graph\constants.h
628+
const static std::unordered_map<std::string, std::string> s_providerNameToExecutionProvider{
629+
{"QNN", "QNNExecutionProvider"},
630+
{"WebGPU", "WebGpuExecutionProvider"},
631+
{"OpenVINO", "OpenVINOExecutionProvider"},
632+
{"VitisAI", "VitisAIExecutionProvider"},
633+
{"NvTensorRtRtx", "NvTensorRTRTXExecutionProvider"},
634+
};
635+
std::string epName{};
636+
if (auto search = s_providerNameToExecutionProvider.find(provider_options.name); search != s_providerNameToExecutionProvider.end()) {
637+
epName = search->second;
638+
}
639+
640+
size_t num_devices;
641+
const OrtEpDevice* const* device_ptrs;
642+
Ort::api->GetEpDevices(&GetOrtEnv(), &device_ptrs, &num_devices);
643+
std::vector<const OrtEpDevice*> ep_devices_ptrs;
644+
ep_devices_ptrs.reserve(num_devices);
645+
for (size_t i = 0; i < num_devices; ++i) {
646+
const OrtHardwareDevice* hardware_device = Ort::api->EpDevice_Device(device_ptrs[i]);
647+
const uint32_t hardware_device_id = Ort::api->HardwareDevice_DeviceId(hardware_device);
648+
const uint32_t hardware_vendor_id = Ort::api->HardwareDevice_VendorId(hardware_device);
649+
const OrtHardwareDeviceType hardware_device_type = Ort::api->HardwareDevice_Type(hardware_device);
650+
651+
auto check_ov_device_type = [&config_ov_device_type, &provider_options](const OrtEpDevice* device_ptr) -> bool {
652+
if (provider_options.name != "OpenVINO") {
653+
return true;
654+
} else if (!config_ov_device_type.has_value()) {
655+
return true;
656+
} else {
657+
const OrtKeyValuePairs* keyvals = Ort::api->EpDevice_EpMetadata(device_ptr);
658+
size_t num_entries;
659+
const char* const* keys = nullptr;
660+
const char* const* values = nullptr;
661+
Ort::api->GetKeyValuePairs(keyvals, &keys, &values, &num_entries);
662+
for (int kvi = 0; kvi < num_entries; kvi++) {
663+
const std::string key = keys[kvi];
664+
const std::string val = values[kvi];
665+
if (key == "ov_device" && val == config_ov_device_type) {
666+
return true;
667+
}
668+
}
669+
return false;
670+
}
671+
};
672+
bool hardware_device_id_matched = (!config_device_id.has_value()) || config_device_id.value() == hardware_device_id;
673+
bool hardware_vendor_id_matched = (!config_vendor_id.has_value()) || config_vendor_id.value() == hardware_vendor_id;
674+
bool hardware_device_type_matched = (!config_device_type_enum.has_value()) ||
675+
config_device_type_enum.value() == hardware_device_type;
676+
bool hardware_ov_device_type_matched = check_ov_device_type(device_ptrs[i]);
677+
678+
// Append matched EP device
679+
if (Ort::api->EpDevice_EpName(device_ptrs[i]) == epName &&
680+
hardware_device_id_matched &&
681+
hardware_vendor_id_matched &&
682+
hardware_device_type_matched &&
683+
hardware_ov_device_type_matched) {
684+
ep_devices_ptrs.push_back(device_ptrs[i]);
685+
// WinML Hotfix: DML and WebGPU EP factories currently only support one device at a time
686+
// OpenVINO also supports only one device at a time
687+
if (provider_options.name == "DML" || provider_options.name == "WebGPU" || provider_options.name == "OpenVINO") {
688+
break;
689+
}
690+
}
691+
}
692+
693+
// No need to append if we can't find a device.
694+
if (!ep_devices_ptrs.empty()) {
695+
std::vector<const char*> keys, values;
696+
for (auto& option : provider_options.options) {
697+
// WinML Hotfix: remove backend_type and backend_path from QNN provider options
698+
static const std::set<std::string> qnn_options_to_remove{"backend_type", "backend_path"};
699+
if (provider_options.name == "QNN" &&
700+
qnn_options_to_remove.find(option.first) != qnn_options_to_remove.end()) {
701+
continue;
702+
}
703+
704+
// 'device_type' is not a supported option for OpenVINO when SessionOptionsAppendExecutionProvider_V2 is used.
705+
if (provider_options.name == "OpenVINO" && option.first == "device_type") {
706+
continue;
707+
}
708+
keys.emplace_back(option.first.c_str());
709+
values.emplace_back(option.second.c_str());
710+
}
711+
712+
Ort::api->SessionOptionsAppendExecutionProvider_V2(
713+
&session_options,
714+
&GetOrtEnv(),
715+
ep_devices_ptrs.data(), ep_devices_ptrs.size(),
716+
keys.data(), values.data(), keys.size());
717+
}
718+
#else
592719
std::vector<const char*> keys, values;
593720
for (auto& option : provider_options.options) {
594721
keys.emplace_back(option.first.c_str());
595722
values.emplace_back(option.second.c_str());
596723
}
597724
session_options.AppendExecutionProvider(provider_options.name.c_str(), keys.data(), values.data(), keys.size());
725+
726+
#endif
598727
}
599728
}
600729
return p_device;

src/python/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${python_srcs})
2626
if(BUILD_WHEEL)
2727
set(WHEEL_FILES_DIR "${CMAKE_BINARY_DIR}/wheel")
2828
message("Setting up wheel files in : ${WHEEL_FILES_DIR}")
29-
if(USE_CUDA)
29+
if(USE_WINML)
30+
set(TARGET_NAME "onnxruntime-genai-winml")
31+
elseif(USE_CUDA)
3032
set(TARGET_NAME "onnxruntime-genai-cuda")
3133
elseif(USE_ROCM)
3234
set(TARGET_NAME "onnxruntime-genai-rocm")
@@ -45,6 +47,7 @@ if(BUILD_WHEEL)
4547
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/package_description.md" DESTINATION ${WHEEL_FILES_DIR}/)
4648
file(COPY "${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt" DESTINATION ${WHEEL_TARGET_NAME}/)
4749
file(COPY "${CMAKE_SOURCE_DIR}/LICENSE" DESTINATION ${WHEEL_TARGET_NAME}/)
50+
4851
add_custom_command(TARGET python POST_BUILD
4952
COMMAND ${CMAKE_COMMAND} -E copy
5053
${ortgenai_embed_libs} $<TARGET_FILE:python>

src/python/py/_dll_directory.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ def add_onnxruntime_dependency(package_id: str):
4040
import ctypes
4141
_ = ctypes.CDLL(dml_path)
4242

43+
# temporary workaround for onnxruntime.dll loading
44+
ort_path = os.path.join(ort_package_path, "capi", "onnxruntime.dll")
45+
# The dependent onnxruntime package may have multiple execution providers.
46+
# Check to see if onnxruntime.dll exists before trying to load it.
47+
if os.path.exists(ort_path):
48+
import ctypes
49+
_ = ctypes.CDLL(ort_path)
50+
4351
elif _is_linux() or _is_macos():
4452
import importlib.util
4553
import ctypes

src/python/setup.py.in

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if not path.exists(long_description_file):
1919
with open(long_description_file, encoding="utf-8") as fdesc:
2020
long_description = fdesc.read()
2121

22-
extras = ["ThirdPartyNotices.txt", "LICENSE"]
22+
extras = ["ThirdPartyNotices.txt", "LICENSE", "MSIX/**/*.msix"]
2323
package_name = '@TARGET_NAME@'
2424

2525

@@ -46,6 +46,13 @@ def _onnxruntime_dependency() -> str:
4646

4747
return dependency if not ort_version else dependency + ">=" + ort_version
4848

49+
def _get_install_requires():
50+
install_requires = [
51+
'numpy>=1.21.6',
52+
]
53+
if package_name != "onnxruntime-genai-winml":
54+
install_requires.append(_onnxruntime_dependency())
55+
return install_requires
4956

5057
setup(
5158
name=package_name,
@@ -56,10 +63,7 @@ setup(
5663
packages=['onnxruntime_genai', 'onnxruntime_genai.models'],
5764
include_package_data=True,
5865
package_data={'': ['*.pyd', '*.dll', '*.so*', '*.dylib'] + extras},
59-
install_requires=[
60-
'numpy>=1.21.6',
61-
_onnxruntime_dependency(),
62-
],
66+
install_requires=_get_install_requires(),
6367
distclass=BinaryDistribution,
6468
author="Microsoft Corporation",
6569
author_email="onnxruntime-genai@microsoft.com",

0 commit comments

Comments
 (0)