From dc5fd27f9e83e1f441e52c62185c804b6ac63fa1 Mon Sep 17 00:00:00 2001 From: Rufan Date: Sun, 12 Jul 2026 15:46:51 +0200 Subject: [PATCH 1/3] fix(reco-detect,reco-autocam,reco-gui): enable DirectML execution provider on Windows AI tracking during file export was silently running on CPU instead of GPU: ort_session.rs tries DirectML first and logs "ORT: DirectML execution provider enabled" regardless of outcome, masking the real `ort::ep` warning ("Couldn't register DmlExecutionProvider... its corresponding Cargo feature is not enabled") logged separately by the ort crate itself. Root cause: the `ort` dependency never had its `directml` feature turned on anywhere in the workspace, so the native DirectML backend was never compiled in - CPU-bound inference every N frames then stalled the whole stitch/encode pipeline between detections, which read as "GPU not fully utilized" during export. Added a `directml` feature through the existing per-backend forwarding chain (mirrors cuda/tensorrt/coreml): reco-detect -> reco-autocam -> reco-gui, enabled only for Windows via a target-specific dependency override so non-Windows builds are unaffected. Verified with a headless RECO_AUTOLOAD/RECO_AUTOEXPORT test export: log now shows `ort::ep: Successfully registered 'DmlExecutionProvider'` (previously a WARN), and nvidia-smi sampling during export went from bursty 2%<->99% GPU utilization to a sustained 21-60%. Co-Authored-By: Claude Sonnet 5 --- crates/reco-autocam/Cargo.toml | 1 + crates/reco-detect/Cargo.toml | 1 + crates/reco-gui/Cargo.toml | 11 +++++++++++ 3 files changed, 13 insertions(+) diff --git a/crates/reco-autocam/Cargo.toml b/crates/reco-autocam/Cargo.toml index 7523131d..97e6ad91 100644 --- a/crates/reco-autocam/Cargo.toml +++ b/crates/reco-autocam/Cargo.toml @@ -30,6 +30,7 @@ ort = ["reco-detect/ort"] cuda = ["reco-detect/cuda"] tensorrt = ["reco-detect/tensorrt"] coreml = ["reco-detect/coreml"] +directml = ["reco-detect/directml"] load-dynamic = ["reco-detect/load-dynamic"] tensorrt-native = ["reco-detect/tensorrt-native"] ncnn = ["reco-detect/ncnn"] diff --git a/crates/reco-detect/Cargo.toml b/crates/reco-detect/Cargo.toml index d0d5afe8..83dd395e 100644 --- a/crates/reco-detect/Cargo.toml +++ b/crates/reco-detect/Cargo.toml @@ -15,6 +15,7 @@ ort = ["dep:ort", "dep:dirs"] cuda = ["ort", "ort/cuda"] tensorrt = ["ort", "ort/tensorrt"] coreml = ["ort", "ort/coreml"] +directml = ["ort", "ort/directml"] load-dynamic = ["ort", "ort/load-dynamic", "dep:libloading"] tensorrt-native = [] ncnn = [] diff --git a/crates/reco-gui/Cargo.toml b/crates/reco-gui/Cargo.toml index 0be47fba..4ccbd6fe 100644 --- a/crates/reco-gui/Cargo.toml +++ b/crates/reco-gui/Cargo.toml @@ -24,6 +24,7 @@ tensorrt-native = ["autocam", "reco-autocam/tensorrt-native"] ncnn = ["autocam", "reco-autocam/ncnn"] cuda = ["autocam", "reco-autocam/cuda"] coreml = ["autocam", "reco-autocam/coreml"] +directml = ["autocam", "reco-autocam/directml"] load-dynamic = ["autocam", "reco-autocam/load-dynamic"] profiling = ["reco-core/profiling", "reco-io/profiling", "reco-calibrate/profiling"] # Headless dev/test preload + auto-export hook (RECO_AUTOLOAD/RECO_AUTOEXPORT). @@ -61,5 +62,15 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true } tracing-log = { workspace = true } +# DirectML EP (Windows-only, see reco-detect/src/ort_session.rs) needs the +# `directml` cargo feature on `ort` compiled in, or it silently no-ops back +# to CPU inference at runtime (the "its corresponding Cargo feature is not +# enabled" ort::ep warning) despite logging as if it succeeded. Enabled +# here rather than in the cross-platform `default` feature list above so +# non-Windows builds aren't affected. +[target.'cfg(windows)'.dependencies] +reco-detect = { path = "../reco-detect", default-features = false, features = ["directml"] } +reco-autocam = { path = "../reco-autocam", optional = true, default-features = false, features = ["directml"] } + [build-dependencies] slint-build = "1.15" From 382e5b84d5e838f6124cbc765b4d3ec4837bb3b2 Mon Sep 17 00:00:00 2001 From: Rufan Date: Wed, 8 Jul 2026 22:53:14 +0200 Subject: [PATCH 2/3] fix(reco-gui,rig-calib): default Slint's shared wgpu instance to reco-core's backend policy rig-calib crashed on startup on a Windows laptop with a broken/partial Vulkan loader (`ReadDataFilesInRegistry` layer-manifest failures): debug builds enable wgpu's DEBUG instance flag, and when Slint's auto-selected Vulkan backend couldn't resolve `vkCmdBeginDebugUtilsLabelEXT`, ash panicked across an FFI boundary that can't unwind, aborting the process. reco-core's own `GpuContext::select_backends()` already avoids this class of bug by preferring DX12 on Windows (a prior fix for AMD Vulkan driver crashes during instance creation), but that policy only applied to the `wgpu::Instance` reco-core creates itself for headless consumers - reco-gui and rig-calib instead hand instance creation off to Slint (`BackendSelector::require_wgpu_28`), which was left to its own Vulkan-first default. Made `select_backends()` public and pass it as `WGPUConfiguration::Automatic::backends` in both, so the shared Slint-owned instance follows the same (still `WGPU_BACKEND`-overridable) policy. Verified: rebuilt and ran rig-calib on the affected laptop - no more Vulkan warnings or panic. Co-Authored-By: Claude Sonnet 5 --- crates/reco-core/src/gpu/mod.rs | 8 +++++++- crates/reco-gui/src/main.rs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/reco-core/src/gpu/mod.rs b/crates/reco-core/src/gpu/mod.rs index 7cee089a..0b1cbef5 100644 --- a/crates/reco-core/src/gpu/mod.rs +++ b/crates/reco-core/src/gpu/mod.rs @@ -249,7 +249,13 @@ impl GpuContext { /// Checks `WGPU_BACKEND` first (user override). Otherwise uses /// platform defaults: DX12 on Windows, Vulkan on Linux, Metal /// on macOS. - fn select_backends() -> wgpu::Backends { + /// + /// Public so windowed consumers (reco-gui, rig-calib) that let Slint + /// create the shared `wgpu::Instance` can apply the same policy via + /// `WGPUConfiguration::Automatic::backends`, instead of falling back to + /// Slint's own default (which tries Vulkan first on Windows and can hit + /// driver/loader bugs this function's platform defaults are chosen to avoid). + pub fn select_backends() -> wgpu::Backends { if let Ok(val) = std::env::var("WGPU_BACKEND") { match val.to_lowercase().as_str() { "vulkan" | "vk" => return wgpu::Backends::VULKAN, diff --git a/crates/reco-gui/src/main.rs b/crates/reco-gui/src/main.rs index 25ae41ca..494bf94c 100644 --- a/crates/reco-gui/src/main.rs +++ b/crates/reco-gui/src/main.rs @@ -1490,6 +1490,7 @@ fn main() -> anyhow::Result<()> { let mut config = slint::wgpu_28::WGPUConfiguration::default(); if let slint::wgpu_28::WGPUConfiguration::Automatic(ref mut settings) = config { settings.device_required_limits = reco_core::wgpu::Limits::downlevel_defaults(); + settings.backends = reco_core::gpu::GpuContext::select_backends(); } config }) From 7b8cd182385614ebd1d4c7974951fe7fa993804b Mon Sep 17 00:00:00 2001 From: Rufan Date: Sun, 12 Jul 2026 15:47:17 +0200 Subject: [PATCH 3/3] fix(reco-obs): fix two Windows build.rs portability bugs reco-obs never built on Windows before (only ever exercised on Linux/ macOS), and hit two real bugs once actually tried against libobs 30.0.2 headers with OBS_INCLUDE_DIR set: - bindgen's clang runs in MSVC-compatibility mode on Windows, which is needed for the rest of the libobs/Windows headers to parse, but flips `util/util_uint64.h` into a branch calling the MSVC-only `_udiv128` intrinsic - not declared by clang's own `intrin.h` shim, so parsing failed. Shimmed just that call away via a targeted clang macro rather than dropping MSVC-compat mode (which broke clang's own SSE/SSE2 intrinsic headers when tried). - `blog_shim.c`'s `#include ` silently relied on `/usr/include` being a default compiler search path on Linux/macOS to resolve the Debian libobs-dev `.../obs/` layout; Windows has no such default. build.rs now also adds the parent of OBS_INCLUDE_DIR to the shim's include path when the layout matches that convention. Both fixes are no-ops on Linux/macOS. Co-Authored-By: Claude Sonnet 5 --- crates/reco-obs/build.rs | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/crates/reco-obs/build.rs b/crates/reco-obs/build.rs index 4680c8b6..1ff1717a 100644 --- a/crates/reco-obs/build.rs +++ b/crates/reco-obs/build.rs @@ -74,6 +74,17 @@ fn main() { .clang_arg(format!("-I{include_dir}")) // libobs headers are C, and we want standard types (not ptrdiff). .clang_arg("-std=c11") + // On Windows, bindgen's clang runs in MSVC-compatibility mode + // (`_MSC_VER` defined), which is required for the rest of the + // libobs/Windows headers to parse correctly. But it also flips + // `util/util_uint64.h`'s `util_mul_div64` into a branch that + // calls `_udiv128` - an MSVC 2019+ intrinsic clang's own + // `intrin.h` shim doesn't declare, so parsing fails. Rather than + // drop MSVC-compat mode (which breaks clang's SSE/SSE2 intrinsic + // headers pulled in transitively), shim just this one call away: + // bindgen doesn't need the `static inline` body to be correct or + // linkable, only for the header to parse. + .clang_arg("-D_udiv128(hi,lo,div,rem)=0") // Only generate bindings for items we actually use. Bindgen // pulls transitive types automatically, so the struct fields // are covered even if the field type isn't in the allowlist. @@ -152,13 +163,24 @@ fn main() { // logger through a fixed-arity entry point. libobs is loaded // into the process by OBS before any plugin, so the shim's // `blog(...)` reference resolves at plugin load time without - // us needing to `-lobs` here. `cc` picks up system headers - // automatically on Linux/macOS. + // us needing to `-lobs` here. let shim_path = PathBuf::from("src/blog_shim.c"); println!("cargo:rerun-if-changed={}", shim_path.display()); - cc::Build::new() - .file(&shim_path) - .include(&include_dir) + let mut shim_build = cc::Build::new(); + shim_build.file(&shim_path).include(&include_dir); + // The shim includes `` (the Debian libobs-dev + // layout: headers rooted under an `obs/` folder). On Linux/macOS + // this resolves for free because `/usr/include` - the usual parent + // of `/usr/include/obs` - is already on the compiler's default + // system search path. Windows has no such default, so add the + // parent of `include_dir` explicitly wherever the layout follows + // that `.../obs` convention (harmless no-op add on Linux/macOS). + if include_dir.ends_with("obs") + && let Some(parent) = PathBuf::from(&include_dir).parent() + { + shim_build.include(parent); + } + shim_build .flag_if_supported("-std=c11") // OBS ships headers that rely on GNU extensions (typeof // etc.) on Linux. `-D_GNU_SOURCE` avoids any surprises.