//! Windows-specific CUDA support. //! //! This module provides Windows platform detection and CUDA library discovery. use std::path::PathBuf; /// Windows CUDA library paths and detection. #[derive(Debug, Clone)] pub struct WindowsCudaConfig { /// CUDA installation path (e.g., C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.x) pub cuda_path: Option, /// cuDNN installation path pub cudnn_path: Option, /// Detected CUDA version pub cuda_version: Option, /// Available CUDA DLLs pub available_dlls: Vec, } impl WindowsCudaConfig { /// Detect CUDA installation on Windows. #[cfg(target_os = "windows")] pub fn detect() -> Self { let cuda_path = Self::find_cuda_path(); let cudnn_path = Self::find_cudnn_path(); let cuda_version = Self::detect_cuda_version(&cuda_path); let available_dlls = Self::find_cuda_dlls(&cuda_path); Self { cuda_path, cudnn_path, cuda_version, available_dlls, } } /// Detect CUDA installation (stub for non-Windows). #[cfg(not(target_os = "windows"))] pub fn detect() -> Self { Self { cuda_path: None, cudnn_path: None, cuda_version: None, available_dlls: Vec::new(), } } /// Find CUDA installation path from environment or default locations. #[cfg(target_os = "windows")] fn find_cuda_path() -> Option { // Check CUDA_PATH environment variable first if let Ok(path) = std::env::var("CUDA_PATH") { let p = PathBuf::from(&path); if p.exists() { return Some(p); } } // Check CUDA_HOME (alternate environment variable) if let Ok(path) = std::env::var("CUDA_HOME") { let p = PathBuf::from(&path); if p.exists() { return Some(p); } } // Check default installation locations let default_paths = [ r"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.6", r"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.5", r"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4", r"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.3", r"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.2", r"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1", r"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.0", r"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8", ]; for path_str in &default_paths { let path = PathBuf::from(path_str); if path.exists() { return Some(path); } } None } #[cfg(not(target_os = "windows"))] fn find_cuda_path() -> Option { None } /// Find cuDNN installation path. #[cfg(target_os = "windows")] fn find_cudnn_path() -> Option { // cuDNN is usually installed alongside CUDA or in CUDNN_PATH if let Ok(path) = std::env::var("CUDNN_PATH") { let p = PathBuf::from(&path); if p.exists() { return Some(p); } } // Check if cuDNN is in CUDA path if let Some(cuda_path) = Self::find_cuda_path() { let cudnn_dll = cuda_path.join("bin").join("cudnn64_9.dll"); if cudnn_dll.exists() { return Some(cuda_path); } // Check older versions let cudnn_dll = cuda_path.join("bin").join("cudnn64_8.dll"); if cudnn_dll.exists() { return Some(cuda_path); } } None } #[cfg(not(target_os = "windows"))] fn find_cudnn_path() -> Option { None } /// Detect CUDA version from installation. #[cfg(target_os = "windows")] fn detect_cuda_version(cuda_path: &Option) -> Option { if let Some(path) = cuda_path { // Try to read version from version.txt let version_file = path.join("version.txt"); if let Ok(content) = std::fs::read_to_string(&version_file) { // Parse "CUDA Version 12.x.y" format if let Some(version) = content.lines().next() { if let Some(v) = version.strip_prefix("CUDA Version ") { return Some(v.trim().to_string()); } } } // Fallback: extract from path if let Some(name) = path.file_name() { if let Some(name_str) = name.to_str() { if let Some(version) = name_str.strip_prefix("v") { return Some(version.to_string()); } } } } None } #[cfg(not(target_os = "windows"))] fn detect_cuda_version(_cuda_path: &Option) -> Option { None } /// Find available CUDA DLLs. #[cfg(target_os = "windows")] fn find_cuda_dlls(cuda_path: &Option) -> Vec { let mut dlls = Vec::new(); if let Some(path) = cuda_path { let bin_path = path.join("bin"); if bin_path.exists() { if let Ok(entries) = std::fs::read_dir(&bin_path) { for entry in entries.flatten() { if let Some(name) = entry.file_name().to_str() { if name.ends_with(".dll") { dlls.push(name.to_string()); } } } } } } // Also check System32 for driver DLLs let system32 = PathBuf::from(r"C:\Windows\System32"); let driver_dlls = ["nvcuda.dll", "nvml.dll", "nvapi64.dll"]; for dll in &driver_dlls { if system32.join(dll).exists() { dlls.push((*dll).to_string()); } } dlls } #[cfg(not(target_os = "windows"))] fn find_cuda_dlls(_cuda_path: &Option) -> Vec { Vec::new() } /// Check if CUDA is available on Windows. pub fn is_available(&self) -> bool { self.cuda_path.is_some() && self.available_dlls.contains(&"nvcuda.dll".to_string()) } /// Get the library search paths for CUDA. pub fn library_paths(&self) -> Vec { let mut paths = Vec::new(); if let Some(cuda_path) = &self.cuda_path { paths.push(cuda_path.join("bin")); paths.push(cuda_path.join("lib").join("x64")); } if let Some(cudnn_path) = &self.cudnn_path { if cudnn_path != self.cuda_path.as_ref().unwrap_or(&PathBuf::new()) { paths.push(cudnn_path.join("bin")); paths.push(cudnn_path.join("lib").join("x64")); } } paths } /// Get required DLLs for CUDA operations. pub fn required_dlls() -> &'static [&'static str] { &[ "nvcuda.dll", // CUDA driver "nvrtc64_120_0.dll", // NVRTC (runtime compilation) "cublas64_12.dll", // cuBLAS "cublasLt64_12.dll", // cuBLAS Lt ] } /// Get optional DLLs for enhanced functionality. pub fn optional_dlls() -> &'static [&'static str] { &[ "cudnn64_9.dll", // cuDNN "cusparse64_12.dll", // cuSPARSE "cusolver64_11.dll", // cuSOLVER "cufft64_11.dll", // cuFFT "curand64_10.dll", // cuRAND ] } /// Check if all required DLLs are available. pub fn has_required_dlls(&self) -> bool { // Only nvcuda.dll is strictly required (driver) self.available_dlls.contains(&"nvcuda.dll".to_string()) } } /// Windows GPU information from DXGI. #[derive(Debug, Clone)] pub struct WindowsGpuInfo { /// GPU name pub name: String, /// Vendor ID pub vendor_id: u32, /// Device ID pub device_id: u32, /// Dedicated video memory in bytes pub dedicated_memory: usize, /// Is NVIDIA GPU pub is_nvidia: bool, /// Is AMD GPU pub is_amd: bool, /// Is Intel GPU pub is_intel: bool, } impl WindowsGpuInfo { /// Enumerate all GPUs on Windows using DXGI. #[cfg(target_os = "windows")] pub fn enumerate_gpus() -> Vec { // This would use DXGI to enumerate adapters // For now, return empty (actual implementation requires windows-sys) Vec::new() } #[cfg(not(target_os = "windows"))] pub fn enumerate_gpus() -> Vec { Vec::new() } /// NVIDIA vendor ID pub const NVIDIA_VENDOR_ID: u32 = 0x10DE; /// AMD vendor ID pub const AMD_VENDOR_ID: u32 = 0x1002; /// Intel vendor ID pub const INTEL_VENDOR_ID: u32 = 0x8086; } #[cfg(test)] mod tests { use super::*; #[test] fn test_cuda_config_detection() { let config = WindowsCudaConfig::detect(); println!("CUDA path: {:?}", config.cuda_path); println!("CUDA version: {:?}", config.cuda_version); println!("Available DLLs: {:?}", config.available_dlls); // On non-Windows, these should be None/empty #[cfg(not(target_os = "windows"))] { assert!(config.cuda_path.is_none()); assert!(config.available_dlls.is_empty()); } } #[test] fn test_required_dlls() { let required = WindowsCudaConfig::required_dlls(); assert!(required.contains(&"nvcuda.dll")); } #[test] fn test_gpu_enumeration() { let gpus = WindowsGpuInfo::enumerate_gpus(); println!("Found {} GPUs", gpus.len()); for gpu in &gpus { println!( " - {} (NVIDIA: {}, AMD: {}, Intel: {})", gpu.name, gpu.is_nvidia, gpu.is_amd, gpu.is_intel ); } } }