XFeat's two exports are a Keypoints role now; the crate no longer names tract, and the app compiles TensorRT engines for both ahead of the first merge. The probe picks the smallest *detector* rather than the smallest file: the tablet's first run chose the 112 KB eye classifier, which has no int8 form, and reported the Hexagon as failed for want of one.
99 lines
3.9 KiB
Rust
99 lines
3.9 KiB
Rust
//! 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: {
|
|
let [landscape, portrait] = dr_pano::xfeat::embedded_model_bytes();
|
|
vec![
|
|
(Role::Segmenter, dr_segment::embedded_model_bytes()),
|
|
(Role::Keypoints, landscape),
|
|
(Role::Keypoints, portrait),
|
|
]
|
|
},
|
|
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)
|
|
}
|