Start the inference engine from both apps and show its choice in Settings
The desktop names where a package may have put libonnxruntime — an override variable, beside the executable, the package's own library directory, the Flatpak prefix, the system library directory — and Android points at the APK's native library directory, which is also what Qualcomm's DSP loader must be told for the Hexagon skel. Android starts the engine at the end of the model unpack rather than at launch, because the probe fingerprints the model files and a first launch has none until then. The About panel gains an Inference row beside Graphics, re-read every two seconds while the probe runs and engines land, and faces.model_id carries the detector's form: an int8 detector finds a different set of faces and is a different population (docs/inference.md §7). A low-memory signal drops every idle session with the GPU caches. The APK assembly bundles ONNX Runtime and the Qualcomm HTP libraries from Maven, fetched by tools/fetch-android-runtime.sh with their published checksums; RUNTIME_DIR=none builds the tract-only APK, which is a slower app and not a broken one. The desktop packages carry no runtime yet. Two probe fixes from the first desktop run: the floor must not be built with CPU fallback disabled, and a versioned libonnxruntime.so is a runtime too. On the reference desktop the probe now loads ONNX Runtime 1.30, measures 30 ms on the CPU provider, and selects TensorRT at 1.5 ms.
This commit is contained in:
@@ -27,7 +27,7 @@ use crate::{AppWindow, IdentityFace, IdentityPerson};
|
||||
/// use rather than captured once, because the page can change it while the
|
||||
/// screen is open.
|
||||
pub fn model_id(settings: &crate::settings_ui::SettingsController) -> String {
|
||||
settings.snapshot().faces.detector.model_id().to_string()
|
||||
crate::inference::model_id(settings.snapshot().faces.detector).to_string()
|
||||
}
|
||||
|
||||
/// Screen state that outlives a single callback.
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//! The app's side of `dr-inference-engine` (docs/inference.md §8).
|
||||
//!
|
||||
//! What lives here is what only the app knows: where the runtime file might
|
||||
//! be, where the disposable cache goes, which model files this device has,
|
||||
//! and how the engine's status becomes a line on the settings page. What
|
||||
//! runs the models does not.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use dr_inference_engine::{Form, Role, Status};
|
||||
use dr_types::FaceDetector;
|
||||
|
||||
/// Start the engine: choose the runtime, probe in the background, compile
|
||||
/// engines for whatever this device turns out to have.
|
||||
///
|
||||
/// `runtime_dirs` is where the platform put `libonnxruntime`: an empty list
|
||||
/// is the tract build. Called once, after the models are on disk — on
|
||||
/// Android that is the end of `install_bundled_models`, since the probe
|
||||
/// fingerprints the model files and a probe before they land would be a
|
||||
/// probe of nothing.
|
||||
pub fn init(runtime_dirs: Vec<PathBuf>) {
|
||||
let dir = crate::library::shared_face_models_dir();
|
||||
let mut models: Vec<(Role, PathBuf)> = FaceDetector::ALL
|
||||
.iter()
|
||||
.map(|d| (Role::Detector, dir.join(d.file_name())))
|
||||
.collect();
|
||||
models.push((Role::Embedder, dir.join("arcface_mbf_b1.onnx")));
|
||||
models.push((Role::Scene, dir.join("yolo26s-sem-ade20k.onnx")));
|
||||
models.push((Role::Landmarks, dir.join(crate::library::LANDMARK_MODEL)));
|
||||
models.push((Role::EyeClassifier, dir.join(crate::library::EYE_MODEL)));
|
||||
models.push((
|
||||
Role::EyeClassifier,
|
||||
dir.join(crate::library::SUNGLASSES_MODEL),
|
||||
));
|
||||
models.retain(|(_, p)| p.is_file());
|
||||
|
||||
dr_inference_engine::init(dr_inference_engine::Config {
|
||||
runtime_dirs,
|
||||
cache_dir: crate::library::inference_cache_dir(),
|
||||
models,
|
||||
embedded: vec![(Role::Segmenter, dr_segment::embedded_model_bytes())],
|
||||
ceiling: None,
|
||||
threads: 0,
|
||||
decay: std::time::Duration::ZERO,
|
||||
});
|
||||
|
||||
// A low-memory signal drops every session nobody is mid-run with; the
|
||||
// next use loads again. Same tier as the GPU caches: rebuilt from data
|
||||
// the process still holds, and on a mobile GPU or NPU the largest pool.
|
||||
crate::memory::evict_at(crate::memory::Tier::Gpu, dr_inference_engine::release_all);
|
||||
}
|
||||
|
||||
/// Which form the current backend loads `detector` in, given the files on
|
||||
/// this device — the fact `faces.model_id` has to carry (§7).
|
||||
///
|
||||
/// Reads the shared directory only. An account-private model directory can
|
||||
/// override the file `library::face_models` loads, but not which form the
|
||||
/// backend wants, and the int8 sibling is something a packager ships, not
|
||||
/// something a user drops in.
|
||||
pub fn detector_form(detector: FaceDetector) -> Form {
|
||||
let canonical = crate::library::shared_face_models_dir().join(detector.file_name());
|
||||
dr_inference_engine::resolve_model(Role::Detector, &canonical).1
|
||||
}
|
||||
|
||||
/// The `faces.model_id` this device indexes under with `detector`.
|
||||
pub fn model_id(detector: FaceDetector) -> &'static str {
|
||||
match detector_form(detector) {
|
||||
Form::F32 => detector.model_id(),
|
||||
Form::Int8 => detector.model_id_int8(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The two lines the About panel shows: what is running the models, and
|
||||
/// why or how far along.
|
||||
pub fn about_lines() -> (String, String) {
|
||||
let status: Status = dr_inference_engine::status();
|
||||
let line = status.line();
|
||||
let detail = if status.probing {
|
||||
"Checking what this device can run the models on…".to_string()
|
||||
} else if status.engines.1 > 0 && status.engines.0 < status.engines.1 {
|
||||
format!(
|
||||
"Preparing {} engines · {} of {}",
|
||||
status.rung.label(),
|
||||
status.engines.0,
|
||||
status.engines.1
|
||||
)
|
||||
} else {
|
||||
status.reason
|
||||
};
|
||||
(line, detail)
|
||||
}
|
||||
+30
-3
@@ -39,6 +39,7 @@ pub mod identity;
|
||||
mod identity_ui;
|
||||
mod import;
|
||||
mod import_ui;
|
||||
pub mod inference;
|
||||
mod labels;
|
||||
mod library;
|
||||
mod library_ui;
|
||||
@@ -1209,6 +1210,32 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
None => window.set_backend("NO GPU".into()),
|
||||
}
|
||||
|
||||
// TRACES: FR-INF-1
|
||||
// What the models run on. Re-read every two seconds because the answer
|
||||
// changes twice after launch — when the probe reports and as each
|
||||
// engine lands — and the page is open for longer than either takes.
|
||||
{
|
||||
let set = |w: &AppWindow| {
|
||||
let (line, detail) = inference::about_lines();
|
||||
w.set_inference_backend(line.into());
|
||||
w.set_inference_detail(detail.into());
|
||||
};
|
||||
set(&window);
|
||||
let weak = window.as_weak();
|
||||
let timer = Rc::new(slint::Timer::default());
|
||||
let held = timer.clone();
|
||||
timer.start(
|
||||
slint::TimerMode::Repeated,
|
||||
std::time::Duration::from_secs(2),
|
||||
move || {
|
||||
let _keep = &held;
|
||||
if let Some(w) = weak.upgrade() {
|
||||
set(&w);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// TRACES: NFR-OPS-1
|
||||
// The diagnostics bundle, wired as the two presses the requirement
|
||||
// describes. Preparing gathers the log and the crash records into memory
|
||||
@@ -1638,7 +1665,7 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
library.set_fetch_ahead(stored.cache.fetch_ahead);
|
||||
library.set_write_xmp_sidecars(stored.library.write_xmp_sidecars);
|
||||
library.set_timeline_bars(stored.library.timeline_bars);
|
||||
library.set_face_model_id(stored.faces.detector.model_id());
|
||||
library.set_face_model_id(inference::model_id(stored.faces.detector));
|
||||
}
|
||||
|
||||
// --- the export folder picker ----------------------------------------
|
||||
@@ -1790,7 +1817,7 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
// file is installed, and how much of the library that
|
||||
// pipeline has covered — which for a freshly chosen one is
|
||||
// nothing, and saying so is the point.
|
||||
lib.set_face_model_id(s.faces.detector.model_id());
|
||||
lib.set_face_model_id(inference::model_id(s.faces.detector));
|
||||
if let Some(w) = weak.upgrade() {
|
||||
refresh_face_status(&w, &lib, s.faces.detector);
|
||||
}
|
||||
@@ -3933,7 +3960,7 @@ fn refresh_face_status(
|
||||
window,
|
||||
&library.catalog(),
|
||||
store.as_ref(),
|
||||
detector.model_id(),
|
||||
inference::model_id(detector),
|
||||
models.as_ref().is_some_and(|m| m.eyes.is_some()),
|
||||
);
|
||||
window.set_identity_model_missing(models.is_none());
|
||||
|
||||
@@ -5191,6 +5191,13 @@ impl FaceModelPaths {
|
||||
}
|
||||
}
|
||||
|
||||
/// Where the inference engine keeps what it derives per device: the probe
|
||||
/// result and compiled engines (docs/inference.md §4, §5). A peer of
|
||||
/// `thumbs`, not of the catalog: disposable, regenerable, never synced.
|
||||
pub fn inference_cache_dir() -> PathBuf {
|
||||
data_root().join("inference")
|
||||
}
|
||||
|
||||
/// The detector and embedder files, if both are present — and the eye
|
||||
/// models beside them, if those are.
|
||||
///
|
||||
|
||||
@@ -484,7 +484,9 @@ impl LibraryController {
|
||||
dr_types::LibrarySettings::default().write_xmp_sidecars,
|
||||
),
|
||||
timeline_bars: std::cell::Cell::new(dr_types::LibrarySettings::default().timeline_bars),
|
||||
face_model_id: RefCell::new(dr_types::FaceDetector::default().model_id().to_string()),
|
||||
face_model_id: RefCell::new(
|
||||
crate::inference::model_id(dr_types::FaceDetector::default()).to_string(),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user