#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
use std::sync::OnceLock;
#[cfg(any(target_os = "linux", target_os = "windows"))]
const CUDA_SUCCESS: i32 = 0;
#[cfg(any(target_os = "linux", target_os = "windows"))]
pub fn detect_cuda_runtime() -> bool {
static CUDA_AVAILABLE: OnceLock<bool> = OnceLock::new();
*CUDA_AVAILABLE.get_or_init(probe_cuda_driver)
}
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
pub fn detect_cuda_runtime() -> bool {
false
}
#[cfg(target_os = "linux")]
const CUDA_DRIVER_CANDIDATES: &[&str] = &["libcuda.so.1", "libcuda.so"];
#[cfg(target_os = "windows")]
const CUDA_DRIVER_CANDIDATES: &[&str] = &["nvcuda.dll"];
#[cfg(any(target_os = "linux", target_os = "windows"))]
fn probe_cuda_driver() -> bool {
for name in CUDA_DRIVER_CANDIDATES {
let lib = match unsafe { libloading::Library::new(*name) } {
Ok(lib) => lib,
Err(_) => continue,
};
match probe_loaded_driver(&lib) {
Some(available) => {
std::mem::forget(lib);
return available;
}
None => continue,
}
}
false
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
fn probe_loaded_driver(lib: &libloading::Library) -> Option<bool> {
type CuInitFn = unsafe extern "system" fn(u32) -> i32;
type CuDeviceGetCountFn = unsafe extern "system" fn(*mut i32) -> i32;
let cu_init: libloading::Symbol<'_, CuInitFn> = unsafe { lib.get(b"cuInit\0") }.ok()?;
let cu_device_get_count: libloading::Symbol<'_, CuDeviceGetCountFn> =
unsafe { lib.get(b"cuDeviceGetCount\0") }.ok()?;
let init_status = unsafe { cu_init(0) };
if init_status != CUDA_SUCCESS {
return Some(false);
}
let mut device_count: i32 = 0;
let count_status = unsafe { cu_device_get_count(&mut device_count) };
Some(count_status == CUDA_SUCCESS && device_count > 0)
}
#[cfg(target_os = "macos")]
pub fn detect_metal_runtime() -> bool {
static METAL_AVAILABLE: OnceLock<bool> = OnceLock::new();
*METAL_AVAILABLE.get_or_init(probe_metal)
}
#[cfg(not(target_os = "macos"))]
pub fn detect_metal_runtime() -> bool {
false
}
#[cfg(all(target_os = "macos", feature = "metal"))]
fn probe_metal() -> bool {
metal::Device::system_default().is_some()
}
#[cfg(all(target_os = "macos", not(feature = "metal")))]
fn probe_metal() -> bool {
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detection_does_not_panic_and_is_stable() {
let first_cuda = detect_cuda_runtime();
let second_cuda = detect_cuda_runtime();
assert_eq!(first_cuda, second_cuda);
let first_metal = detect_metal_runtime();
let second_metal = detect_metal_runtime();
assert_eq!(first_metal, second_metal);
}
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
#[test]
fn metal_reported_on_apple_silicon() {
assert!(detect_metal_runtime());
}
#[cfg(target_os = "macos")]
#[test]
fn cuda_never_reported_on_macos() {
assert!(!detect_cuda_runtime());
}
#[cfg(not(target_os = "macos"))]
#[test]
fn metal_never_reported_off_macos() {
assert!(!detect_metal_runtime());
}
#[test]
fn platform_capabilities_reflect_runtime_probes() {
let caps = super::super::types::PlatformCapabilities::detect();
assert_eq!(caps.cuda_available, detect_cuda_runtime());
assert_eq!(caps.metal_available, detect_metal_runtime());
}
}