Files
rustytorch/crates/core/rtx-backend-cuda/src/windows.rs
T
2026-03-04 00:08:42 +00:00

325 lines
9.9 KiB
Rust

//! 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<PathBuf>,
/// cuDNN installation path
pub cudnn_path: Option<PathBuf>,
/// Detected CUDA version
pub cuda_version: Option<String>,
/// Available CUDA DLLs
pub available_dlls: Vec<String>,
}
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<PathBuf> {
// 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<PathBuf> {
None
}
/// Find cuDNN installation path.
#[cfg(target_os = "windows")]
fn find_cudnn_path() -> Option<PathBuf> {
// 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<PathBuf> {
None
}
/// Detect CUDA version from installation.
#[cfg(target_os = "windows")]
fn detect_cuda_version(cuda_path: &Option<PathBuf>) -> Option<String> {
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<PathBuf>) -> Option<String> {
None
}
/// Find available CUDA DLLs.
#[cfg(target_os = "windows")]
fn find_cuda_dlls(cuda_path: &Option<PathBuf>) -> Vec<String> {
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<PathBuf>) -> Vec<String> {
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<PathBuf> {
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<Self> {
// 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<Self> {
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
);
}
}
}