diff --git a/crates/engine/src/analysis/models.rs b/crates/engine/src/analysis/models.rs index a00dd660e..d166702ac 100644 --- a/crates/engine/src/analysis/models.rs +++ b/crates/engine/src/analysis/models.rs @@ -420,7 +420,13 @@ fn list(s: &mut Session, task: Task) -> Result { "active": st.active.is_none(), })]; for m in seg::models(task) { + #[cfg(not(target_arch = "wasm32"))] + let (recommended, hint) = (effectcraft_sam2::mask::recommended().id == m.id, effectcraft_sam2::mask::hint(m.id)); + #[cfg(target_arch = "wasm32")] + let (recommended, hint) = (false, None::); v.push(json!({ + "recommended": recommended, + "hint": hint, "id": m.id, "name": m.name, "description": m.description, diff --git a/crates/sam2/src/mask.rs b/crates/sam2/src/mask.rs index 251bb05ca..d840d4052 100644 --- a/crates/sam2/src/mask.rs +++ b/crates/sam2/src/mask.rs @@ -148,6 +148,27 @@ pub fn load(bytes: &[u8]) -> std::result::Result, String> { Ok(Arc::new(Sam2Mask::with_info(Arc::new(model), info))) } +/// The size to suggest on this computer: Small when a GPU is usable (no slower to run than Tiny there, and more accurate), +/// Tiny otherwise (the bigger backbones take several seconds a frame on a CPU). +pub fn recommended() -> &'static ModelInfo { + if crate::gpu::Gpu::shared().is_ok() { &effectcraft_segment::SAM2_SMALL } else { &SAM2_TINY } +} + +/// A one-line note for the Settings list: what suits this computer, and what the bigger sizes cost without a GPU. +pub fn hint(id: &str) -> Option { + if !SIZES.iter().any(|m| m.id == id) { + return None; + } + let gpu = crate::gpu::Gpu::shared().is_ok(); + Some(match (id == recommended().id, gpu, id) { + (true, true, _) => "Recommended: a GPU was found, and this size is no slower than Tiny on it.".to_string(), + (true, false, _) => "Recommended: no GPU was found, and this is the quickest on a CPU.".to_string(), + (false, false, "sam2.1-hiera-base-plus" | "sam2.1-hiera-large") => "Slow without a GPU (several seconds a frame); none was found.".to_string(), + (false, true, "sam2.1-hiera-large") => "For difficult footage; about 0.7 s of GPU time to look at each frame.".to_string(), + _ => return None, + }) +} + /// The registry entries this crate loads. pub const SIZES: [&ModelInfo; 4] = [&SAM2_TINY, &effectcraft_segment::SAM2_SMALL, &effectcraft_segment::SAM2_BASE_PLUS, &effectcraft_segment::SAM2_LARGE]; diff --git a/crates/ui-egui/src/panels/settings.rs b/crates/ui-egui/src/panels/settings.rs index 816b0ca01..8b4133fbc 100644 --- a/crates/ui-egui/src/panels/settings.rs +++ b/crates/ui-egui/src/panels/settings.rs @@ -489,6 +489,9 @@ fn models_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, t: &Tokens, acts: &mut ui.label(RichText::new(state).color(if active { t.accent } else { t.text_dim })); }); ui.label(RichText::new(m["description"].as_str().unwrap_or_default()).color(t.text_dim)); + if let Some(hint) = m["hint"].as_str() { + ui.label(RichText::new(hint).color(if m["recommended"].as_bool().unwrap_or(false) { t.accent } else { t.text_dim })); + } if let Some(by) = m["authors"].as_str() { ui.label(RichText::new(format!("By {by}")).color(t.text_dim)); }