Skip to content

Commit 5f0d080

Browse files
mschofiejywu-msft
authored andcommitted
'QnnEpFactory' should provide a fully-qualified path to the backend (microsoft#25407)
### Description The `QnnEpFactory` implementation currently initializes the underlying provider by passing the `backend_type` configuration as `htp`, causing the provider to find the appropriate backend-library, and load it relative to the OnnxRuntime library. But if EP's are distributed separately from the OnnxRuntime library - a major benefit of the EP ABI - then the backend-library may-well not be relative to the OnnxRuntime. Having the `QnnEpFactory` implementation look for its associated runtime relative to _itself_ would allow the implementation to bring its own runtime - and that's what this PR enables. If the `QnnEpFactory` implementation is co-located with the OnnxRuntime library, then this is consistent with the existing behavior, but an `QnnEpFactory` implementation that is shipped 'out-of-band' will use a backend-relative to itself. WinML has been using a version of this fix, and this PR is 'upstreaming' the change. ### Motivation and Context To support out-of-band distribution of EP's - enabled by the EP ABI work - then EP's should accommodate finding dependencies relative to the EP library, and not the OnnxRuntime library. --------- Co-authored-by: George Wu <[email protected]>
1 parent 2d806f8 commit 5f0d080

File tree

1 file changed

+49
-5
lines changed

1 file changed

+49
-5
lines changed

onnxruntime/core/providers/qnn/qnn_provider_factory.cc

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,40 @@
33

44
#include <string>
55
#include <unordered_map>
6+
#include <utility>
67
#include "core/providers/qnn/qnn_provider_factory_creator.h"
78
#include "core/providers/qnn/qnn_execution_provider.h"
89
#include "core/providers/qnn/builder/qnn_utils.h"
910

11+
/// @brief Gets the path of directory containing the dynamic library that contains the address.
12+
/// @param address An address of a function or variable in the dynamic library.
13+
/// @return The path of the directory containing the dynamic library, or an empty string if the path cannot be determined.
14+
static onnxruntime::PathString GetDynamicLibraryLocationByAddress(const void* address) {
15+
#ifdef _WIN32
16+
HMODULE moduleHandle;
17+
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
18+
reinterpret_cast<LPCWSTR>(address), &moduleHandle)) {
19+
return {};
20+
}
21+
std::wstring buffer;
22+
for (std::uint32_t size{70}; size < 4096; size *= 2) {
23+
buffer.resize(size, L'\0');
24+
const std::uint32_t requiredSize = ::GetModuleFileNameW(moduleHandle, buffer.data(), size);
25+
if (requiredSize == 0) {
26+
break;
27+
}
28+
if (requiredSize == size) {
29+
continue;
30+
}
31+
buffer.resize(requiredSize);
32+
return {std::move(buffer)};
33+
}
34+
#else
35+
std::ignore = address;
36+
#endif
37+
return {};
38+
}
39+
1040
namespace onnxruntime {
1141
struct QNNProviderFactory : IExecutionProviderFactory {
1242
QNNProviderFactory(const ProviderOptions& provider_options_map, const ConfigOptions* config_options)
@@ -124,12 +154,12 @@ struct QnnEpFactory : OrtEpFactory {
124154
const OrtLogger& default_logger_in,
125155
const char* ep_name,
126156
OrtHardwareDeviceType hw_type,
127-
const char* qnn_backend_type)
157+
std::string qnn_backend_path)
128158
: ort_api{ort_api_in},
129159
default_logger{default_logger_in},
130160
ep_name{ep_name},
131161
ort_hw_device_type{hw_type},
132-
qnn_backend_type{qnn_backend_type} {
162+
qnn_backend_path{std::move(qnn_backend_path)} {
133163
ort_version_supported = ORT_API_VERSION;
134164
GetName = GetNameImpl;
135165
GetVendor = GetVendorImpl;
@@ -188,7 +218,7 @@ struct QnnEpFactory : OrtEpFactory {
188218
factory->ort_api.HardwareDevice_VendorId(&device) == factory->vendor_id) {
189219
OrtKeyValuePairs* ep_options = nullptr;
190220
factory->ort_api.CreateKeyValuePairs(&ep_options);
191-
factory->ort_api.AddKeyValuePair(ep_options, "backend_type", factory->qnn_backend_type.c_str());
221+
factory->ort_api.AddKeyValuePair(ep_options, "backend_path", factory->qnn_backend_path.c_str());
192222
ORT_API_RETURN_IF_ERROR(
193223
factory->ort_api.GetEpApi()->CreateEpDevice(factory, &device, nullptr, ep_options,
194224
&ep_devices[num_ep_devices++]));
@@ -258,7 +288,7 @@ struct QnnEpFactory : OrtEpFactory {
258288
// Qualcomm vendor ID. Refer to the ACPI ID registry (search Qualcomm): https://uefi.org/ACPI_ID_List
259289
const uint32_t vendor_id{'Q' | ('C' << 8) | ('O' << 16) | ('M' << 24)};
260290
const OrtHardwareDeviceType ort_hw_device_type; // Supported OrtHardwareDevice
261-
const std::string qnn_backend_type; // QNN backend type for OrtHardwareDevice
291+
const std::string qnn_backend_path; // QNN backend path for OrtHardwareDevice
262292
};
263293

264294
extern "C" {
@@ -271,9 +301,23 @@ OrtStatus* CreateEpFactories(const char* /*registration_name*/, const OrtApiBase
271301
const OrtApi* ort_api = ort_api_base->GetApi(ORT_API_VERSION);
272302

273303
// Factory could use registration_name or define its own EP name.
304+
#if defined(_WIN32)
305+
std::string backend_path = "QnnHtp.dll";
306+
#else
307+
std::string backend_path = "libQnnHtp.so";
308+
#endif
309+
310+
// Identify the path of the current dynamic library, and expect that backend_path is in the same directory.
311+
onnxruntime::PathString current_path = GetDynamicLibraryLocationByAddress(reinterpret_cast<const void*>(&CreateEpFactories));
312+
if (!current_path.empty()) {
313+
const std::filesystem::path parent_path = std::filesystem::path{std::move(current_path)}.parent_path();
314+
backend_path = (parent_path / backend_path).string();
315+
}
316+
274317
auto factory_npu = std::make_unique<QnnEpFactory>(*ort_api, *default_logger,
275318
onnxruntime::kQnnExecutionProvider,
276-
OrtHardwareDeviceType_NPU, "htp");
319+
OrtHardwareDeviceType_NPU,
320+
std::move(backend_path));
277321

278322
// If want to support GPU, create a new factory instance because QNN EP is not currently setup to partition a single model
279323
// among heterogeneous devices.

0 commit comments

Comments
 (0)