Give a phone without a Qualcomm SoC the generic WebGPU runtime
The APK's ONNX Runtime is the QNN build, which carries no WebGPU, so a non-Qualcomm phone had nothing above the CPU provider. The APK now also carries Microsoft's stock onnxruntime-android 1.29.0 (32 MB) as libonnxruntime_generic.so, and the app offers it after the QNN build. The runtime search stops at a perfect fit, so on a Qualcomm device the QNN build — listed first — is all that is opened, and the generic build never loads beside it. A Qualcomm SoC is read from ro.soc.manufacturer or, before Android 12, from Qualcomm's FastRPC library being present: a Qualcomm device mistaken for another would trade its Hexagon for the generic rung.
This commit is contained in:
@@ -451,8 +451,13 @@ fn unpack_bundled_models(app: &slint::android::AndroidApp) {
|
|||||||
// on a first launch they were not on disk until this line. The runtime
|
// on a first launch they were not on disk until this line. The runtime
|
||||||
// is in the APK's native library directory beside `libdarkroom.so`,
|
// is in the APK's native library directory beside `libdarkroom.so`,
|
||||||
// which is also where Qualcomm's DSP loader has to be pointed for the
|
// which is also where Qualcomm's DSP loader has to be pointed for the
|
||||||
// Hexagon skel (docs/dev/inference.md §3, §8).
|
// Hexagon skel (docs/dev/inference.md §3, §8). Two of them: the QNN
|
||||||
dr_ui::inference::init(native_library_dir().into_iter().collect());
|
// build, and the generic WebGPU build by its file name, which the
|
||||||
|
// engine opens only when the first does not fit the SoC (§3.2).
|
||||||
|
let runtimes = native_library_dir()
|
||||||
|
.map(|dir| vec![dir.clone(), dir.join("libonnxruntime_generic.so")])
|
||||||
|
.unwrap_or_default();
|
||||||
|
dr_ui::inference::init(runtimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The directory the system unpacked this APK's native libraries into.
|
/// The directory the system unpacked this APK's native libraries into.
|
||||||
|
|||||||
@@ -58,7 +58,10 @@ pub fn ensure_installed() {
|
|||||||
/// Best fit, not first found (§3.2): a device can hold several runtimes —
|
/// Best fit, not first found (§3.2): a device can hold several runtimes —
|
||||||
/// the package's OpenVINO build, a CUDA build the user fetched, the
|
/// the package's OpenVINO build, a CUDA build the user fetched, the
|
||||||
/// distribution's ROCm build — and each carries one vendor's providers.
|
/// distribution's ROCm build — and each carries one vendor's providers.
|
||||||
/// Between equals, the earlier directory wins, as it always has.
|
/// Between equals, the earlier directory wins, as it always has, and a
|
||||||
|
/// runtime that fits perfectly ends the search: the APK's QNN build on a
|
||||||
|
/// Qualcomm tablet is found first, and the generic build beside it is
|
||||||
|
/// never opened there.
|
||||||
/// `DARKROOM_ORT_DIR`, when it loads, wins outright: it is how a person
|
/// `DARKROOM_ORT_DIR`, when it loads, wins outright: it is how a person
|
||||||
/// says which runtime they mean.
|
/// says which runtime they mean.
|
||||||
pub fn install(dirs: &[PathBuf]) -> Runtime {
|
pub fn install(dirs: &[PathBuf]) -> Runtime {
|
||||||
@@ -110,7 +113,11 @@ fn install_best(dirs: &[PathBuf]) -> Option<Runtime> {
|
|||||||
found.push(f);
|
found.push(f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
let perfect = gpus.score(&f.providers) >= crate::hardware::PERFECT;
|
||||||
found.push(f);
|
found.push(f);
|
||||||
|
if perfect {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => log::info!("inference: no runtime in {}: {e}", dir.display()),
|
Err(e) => log::info!("inference: no runtime in {}: {e}", dir.display()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ pub struct Gpus {
|
|||||||
pub qualcomm: bool,
|
pub qualcomm: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The score of a runtime whose vendor rung matches the device's GPU.
|
||||||
|
/// Nothing beats it, so the search stops there.
|
||||||
|
pub const PERFECT: u32 = 3;
|
||||||
|
|
||||||
impl Gpus {
|
impl Gpus {
|
||||||
/// How well a runtime offering `providers` fits this device. The vendor
|
/// How well a runtime offering `providers` fits this device. The vendor
|
||||||
/// rungs score above OpenVINO because a machine with an Intel iGPU and
|
/// rungs score above OpenVINO because a machine with an Intel iGPU and
|
||||||
@@ -29,10 +33,10 @@ impl Gpus {
|
|||||||
providers
|
providers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| match p.as_str() {
|
.map(|p| match p.as_str() {
|
||||||
"TensorrtExecutionProvider" | "CUDAExecutionProvider" if self.nvidia => 3,
|
"TensorrtExecutionProvider" | "CUDAExecutionProvider" if self.nvidia => PERFECT,
|
||||||
"MIGraphXExecutionProvider" if self.amd_rocm => 3,
|
"MIGraphXExecutionProvider" if self.amd_rocm => PERFECT,
|
||||||
"QNNExecutionProvider" if self.qualcomm => 3,
|
"QNNExecutionProvider" if self.qualcomm => PERFECT,
|
||||||
"CoreMLExecutionProvider" => 3,
|
"CoreMLExecutionProvider" => PERFECT,
|
||||||
"OpenVINOExecutionProvider" if self.intel => 2,
|
"OpenVINOExecutionProvider" if self.intel => 2,
|
||||||
"WebGpuExecutionProvider" => 1,
|
"WebGpuExecutionProvider" => 1,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
@@ -91,9 +95,21 @@ pub fn detect() -> Gpus {
|
|||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub fn detect() -> Gpus {
|
pub fn detect() -> Gpus {
|
||||||
|
// `ro.soc.manufacturer` exists from Android 12. Qualcomm's FastRPC
|
||||||
|
// library, which the Hexagon path loads anyway, is the second witness:
|
||||||
|
// a Qualcomm device read as anything else would trade its NPU for the
|
||||||
|
// generic rung.
|
||||||
let soc = crate::probe::system_property("ro.soc.manufacturer");
|
let soc = crate::probe::system_property("ro.soc.manufacturer");
|
||||||
|
let fastrpc = [
|
||||||
|
"/vendor/lib64/libcdsprpc.so",
|
||||||
|
"/system/vendor/lib64/libcdsprpc.so",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.any(|p| std::path::Path::new(p).exists());
|
||||||
Gpus {
|
Gpus {
|
||||||
qualcomm: soc.eq_ignore_ascii_case("QTI") || soc.eq_ignore_ascii_case("Qualcomm"),
|
qualcomm: soc.eq_ignore_ascii_case("QTI")
|
||||||
|
|| soc.eq_ignore_ascii_case("Qualcomm")
|
||||||
|
|| fastrpc,
|
||||||
..Gpus::default()
|
..Gpus::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,10 +263,12 @@ cp "${DEX}" "${OUT}/staging/classes.dex"
|
|||||||
# native library directory. The build links none of it — the app dlopens
|
# native library directory. The build links none of it — the app dlopens
|
||||||
# `libonnxruntime.so` at launch and runs on tract if it is not there — so an
|
# `libonnxruntime.so` at launch and runs on tract if it is not there — so an
|
||||||
# APK without these is a slower app, not a broken one, and `RUNTIME_DIR=none`
|
# APK without these is a slower app, not a broken one, and `RUNTIME_DIR=none`
|
||||||
# builds exactly that. 174 MB for the default set; the script says which
|
# builds exactly that. 206 MB for the default set — 32 MB of it the generic
|
||||||
# Hexagon generations that buys.
|
# WebGPU build for a phone without a Qualcomm SoC; the script says which
|
||||||
|
# Hexagon generations the rest buys.
|
||||||
if [[ "${RUNTIME_DIR}" != "none" ]]; then
|
if [[ "${RUNTIME_DIR}" != "none" ]]; then
|
||||||
if [[ ! -f "${RUNTIME_DIR}/lib/libonnxruntime.so" ]]; then
|
if [[ ! -f "${RUNTIME_DIR}/lib/libonnxruntime.so" \
|
||||||
|
|| ! -f "${RUNTIME_DIR}/lib/libonnxruntime_generic.so" ]]; then
|
||||||
"${REPO}/tools/fetch-android-runtime.sh" "${RUNTIME_DIR}"
|
"${REPO}/tools/fetch-android-runtime.sh" "${RUNTIME_DIR}"
|
||||||
fi
|
fi
|
||||||
cp "${RUNTIME_DIR}"/lib/*.so "${OUT}/staging/lib/${ABI}/"
|
cp "${RUNTIME_DIR}"/lib/*.so "${OUT}/staging/lib/${ABI}/"
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ _None._
|
|||||||
| FR-PLAT-AND-3 | [`core/dr-catalog/src/jobs.rs:1`](../../core/dr-catalog/src/jobs.rs#L1), [`core/dr-catalog/src/runner.rs:1`](../../core/dr-catalog/src/runner.rs#L1), [`core/dr-catalog/tests/every_queued_kind_has_a_consumer.rs:1`](../../core/dr-catalog/tests/every_queued_kind_has_a_consumer.rs#L1) |
|
| FR-PLAT-AND-3 | [`core/dr-catalog/src/jobs.rs:1`](../../core/dr-catalog/src/jobs.rs#L1), [`core/dr-catalog/src/runner.rs:1`](../../core/dr-catalog/src/runner.rs#L1), [`core/dr-catalog/tests/every_queued_kind_has_a_consumer.rs:1`](../../core/dr-catalog/tests/every_queued_kind_has_a_consumer.rs#L1) |
|
||||||
| FR-PLAT-AND-4 | [`core/dr-catalog/src/runner.rs:1`](../../core/dr-catalog/src/runner.rs#L1) |
|
| FR-PLAT-AND-4 | [`core/dr-catalog/src/runner.rs:1`](../../core/dr-catalog/src/runner.rs#L1) |
|
||||||
| FR-PLAT-AND-5 | [`apps/darkroom-android/src/lib.rs:166`](../../apps/darkroom-android/src/lib.rs#L166), [`core/dr-gpu/src/adjust.rs:1472`](../../core/dr-gpu/src/adjust.rs#L1472), [`core/dr-gpu/src/detail.rs:161`](../../core/dr-gpu/src/detail.rs#L161), [`core/dr-gpu/src/detail.rs:533`](../../core/dr-gpu/src/detail.rs#L533), [`core/dr-gpu/tests/detail_stage.rs:415`](../../core/dr-gpu/tests/detail_stage.rs#L415), [`ui/dr-ui/src/develop/render.rs:654`](../../ui/dr-ui/src/develop/render.rs#L654), [`ui/dr-ui/src/identity_ui.rs:125`](../../ui/dr-ui/src/identity_ui.rs#L125), [`ui/dr-ui/src/lib.rs:1309`](../../ui/dr-ui/src/lib.rs#L1309), [`ui/dr-ui/src/lib.rs:1745`](../../ui/dr-ui/src/lib.rs#L1745), [`ui/dr-ui/src/memory.rs:163`](../../ui/dr-ui/src/memory.rs#L163), [`ui/dr-ui/src/memory.rs:1`](../../ui/dr-ui/src/memory.rs#L1) |
|
| FR-PLAT-AND-5 | [`apps/darkroom-android/src/lib.rs:166`](../../apps/darkroom-android/src/lib.rs#L166), [`core/dr-gpu/src/adjust.rs:1472`](../../core/dr-gpu/src/adjust.rs#L1472), [`core/dr-gpu/src/detail.rs:161`](../../core/dr-gpu/src/detail.rs#L161), [`core/dr-gpu/src/detail.rs:533`](../../core/dr-gpu/src/detail.rs#L533), [`core/dr-gpu/tests/detail_stage.rs:415`](../../core/dr-gpu/tests/detail_stage.rs#L415), [`ui/dr-ui/src/develop/render.rs:654`](../../ui/dr-ui/src/develop/render.rs#L654), [`ui/dr-ui/src/identity_ui.rs:125`](../../ui/dr-ui/src/identity_ui.rs#L125), [`ui/dr-ui/src/lib.rs:1309`](../../ui/dr-ui/src/lib.rs#L1309), [`ui/dr-ui/src/lib.rs:1745`](../../ui/dr-ui/src/lib.rs#L1745), [`ui/dr-ui/src/memory.rs:163`](../../ui/dr-ui/src/memory.rs#L163), [`ui/dr-ui/src/memory.rs:1`](../../ui/dr-ui/src/memory.rs#L1) |
|
||||||
| FR-PLAT-AND-6 | [`apps/darkroom-android/src/lib.rs:472`](../../apps/darkroom-android/src/lib.rs#L472) |
|
| FR-PLAT-AND-6 | [`apps/darkroom-android/src/lib.rs:477`](../../apps/darkroom-android/src/lib.rs#L477) |
|
||||||
| FR-PLAT-LIN-1 | [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`platform/dr-plat/src/dirs.rs:1`](../../platform/dr-plat/src/dirs.rs#L1), [`platform/dr-plat/src/storage.rs:344`](../../platform/dr-plat/src/storage.rs#L344), [`ui/dr-ui/src/lib.rs:1486`](../../ui/dr-ui/src/lib.rs#L1486), [`ui/dr-ui/src/preset_store.rs:1`](../../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/settings_store.rs:1`](../../ui/dr-ui/src/settings_store.rs#L1) |
|
| FR-PLAT-LIN-1 | [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`platform/dr-plat/src/dirs.rs:1`](../../platform/dr-plat/src/dirs.rs#L1), [`platform/dr-plat/src/storage.rs:344`](../../platform/dr-plat/src/storage.rs#L344), [`ui/dr-ui/src/lib.rs:1486`](../../ui/dr-ui/src/lib.rs#L1486), [`ui/dr-ui/src/preset_store.rs:1`](../../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/settings_store.rs:1`](../../ui/dr-ui/src/settings_store.rs#L1) |
|
||||||
| FR-PLAT-LIN-2 | [`platform/dr-plat/src/display.rs:1`](../../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../../platform/dr-plat/src/display/x11.rs#L1) |
|
| FR-PLAT-LIN-2 | [`platform/dr-plat/src/display.rs:1`](../../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../../platform/dr-plat/src/display/x11.rs#L1) |
|
||||||
| FR-PLAT-WIN-1 | [`platform/dr-plat/src/dirs.rs:1`](../../platform/dr-plat/src/dirs.rs#L1) |
|
| FR-PLAT-WIN-1 | [`platform/dr-plat/src/dirs.rs:1`](../../platform/dr-plat/src/dirs.rs#L1) |
|
||||||
|
|||||||
@@ -3,17 +3,23 @@
|
|||||||
#
|
#
|
||||||
# ./tools/fetch-android-runtime.sh [DEST]
|
# ./tools/fetch-android-runtime.sh [DEST]
|
||||||
#
|
#
|
||||||
# Two Maven artefacts, pinned to each other by ONNX Runtime's own POM:
|
# Three Maven artefacts, the first two pinned to each other by ONNX Runtime's
|
||||||
|
# own POM:
|
||||||
#
|
#
|
||||||
# com.microsoft.onnxruntime:onnxruntime-android-qnn MIT
|
# com.microsoft.onnxruntime:onnxruntime-android-qnn MIT
|
||||||
# com.qualcomm.qti:qnn-runtime Qualcomm AI Engine Direct SDK licence
|
# com.qualcomm.qti:qnn-runtime Qualcomm AI Engine Direct SDK licence
|
||||||
|
# com.microsoft.onnxruntime:onnxruntime-android MIT
|
||||||
#
|
#
|
||||||
# The first is ONNX Runtime built with the CPU, QNN, XNNPACK, NNAPI and WebGPU
|
# The first is ONNX Runtime built with the CPU and QNN providers; the second
|
||||||
# providers; the second is Qualcomm's HTP backend — the ARM-side compiler and
|
# is Qualcomm's HTP backend — the ARM-side compiler and the per-generation
|
||||||
# the per-generation Hexagon "skel" the DSP loads. Both ship as AARs whose
|
# Hexagon "skel" the DSP loads. The third is Microsoft's stock build, which
|
||||||
# `jni/arm64-v8a/` is what an APK's `lib/arm64-v8a/` wants, so this script
|
# carries WebGPU (Dawn on Vulkan) and the QNN build does not: the generic
|
||||||
# unpacks exactly that and nothing else, plus the licence texts, which travel
|
# rung for a phone without a Qualcomm SoC (§3.2). It lands as
|
||||||
# with the libraries (§3.1).
|
# `libonnxruntime_generic.so` beside the QNN build; the engine opens the QNN
|
||||||
|
# build first, and on a Qualcomm device stops there. All three ship as AARs
|
||||||
|
# whose `jni/arm64-v8a/` is what an APK's `lib/arm64-v8a/` wants, so this
|
||||||
|
# script unpacks exactly that and nothing else, plus the licence texts, which
|
||||||
|
# travel with the libraries (§3.1).
|
||||||
#
|
#
|
||||||
# ## What is and is not taken from the Qualcomm package
|
# ## What is and is not taken from the Qualcomm package
|
||||||
#
|
#
|
||||||
@@ -65,6 +71,8 @@ fetch com.microsoft.onnxruntime onnxruntime-android-qnn "${ORT_VERSION}" \
|
|||||||
"${DEST}/aar/onnxruntime-android-qnn-${ORT_VERSION}.aar"
|
"${DEST}/aar/onnxruntime-android-qnn-${ORT_VERSION}.aar"
|
||||||
fetch com.qualcomm.qti qnn-runtime "${QNN_VERSION}" \
|
fetch com.qualcomm.qti qnn-runtime "${QNN_VERSION}" \
|
||||||
"${DEST}/aar/qnn-runtime-${QNN_VERSION}.aar"
|
"${DEST}/aar/qnn-runtime-${QNN_VERSION}.aar"
|
||||||
|
fetch com.microsoft.onnxruntime onnxruntime-android "${ORT_VERSION}" \
|
||||||
|
"${DEST}/aar/onnxruntime-android-${ORT_VERSION}.aar"
|
||||||
|
|
||||||
# The ONNX Runtime POM names the QNN version it was built against; a pair
|
# The ONNX Runtime POM names the QNN version it was built against; a pair
|
||||||
# that disagrees loads and then fails at the first graph, which is the kind
|
# that disagrees loads and then fails at the first graph, which is the kind
|
||||||
@@ -82,6 +90,8 @@ rm -rf "${DEST}/lib"
|
|||||||
mkdir -p "${DEST}/lib"
|
mkdir -p "${DEST}/lib"
|
||||||
unzip -q -o -j "${DEST}/aar/onnxruntime-android-qnn-${ORT_VERSION}.aar" \
|
unzip -q -o -j "${DEST}/aar/onnxruntime-android-qnn-${ORT_VERSION}.aar" \
|
||||||
'jni/arm64-v8a/libonnxruntime.so' -d "${DEST}/lib"
|
'jni/arm64-v8a/libonnxruntime.so' -d "${DEST}/lib"
|
||||||
|
unzip -q -o -p "${DEST}/aar/onnxruntime-android-${ORT_VERSION}.aar" \
|
||||||
|
'jni/arm64-v8a/libonnxruntime.so' > "${DEST}/lib/libonnxruntime_generic.so"
|
||||||
members=(jni/arm64-v8a/libQnnHtp.so jni/arm64-v8a/libQnnHtpPrepare.so jni/arm64-v8a/libQnnSystem.so)
|
members=(jni/arm64-v8a/libQnnHtp.so jni/arm64-v8a/libQnnHtpPrepare.so jni/arm64-v8a/libQnnSystem.so)
|
||||||
for arch in ${QNN_HTP_ARCHS}; do
|
for arch in ${QNN_HTP_ARCHS}; do
|
||||||
members+=("jni/arm64-v8a/libQnnHtpV${arch}Skel.so" "jni/arm64-v8a/libQnnHtpV${arch}Stub.so")
|
members+=("jni/arm64-v8a/libQnnHtpV${arch}Skel.so" "jni/arm64-v8a/libQnnHtpV${arch}Stub.so")
|
||||||
|
|||||||
Reference in New Issue
Block a user