Skip to main content

trueno/monitor/
backends.rs

1//! wgpu and CUDA backend implementations for GPU monitoring
2
3#[cfg(feature = "cuda-monitor")]
4use super::GpuMemoryMetrics;
5#[cfg(any(all(feature = "gpu", not(target_arch = "wasm32")), feature = "cuda-monitor"))]
6use super::MonitorError;
7#[cfg(any(all(feature = "gpu", not(target_arch = "wasm32")), feature = "cuda-monitor"))]
8use super::{GpuBackend, GpuDeviceInfo, GpuVendor};
9
10// ============================================================================
11// wgpu Backend Implementation
12// ============================================================================
13
14/// Query device info from wgpu adapter
15#[cfg(all(feature = "gpu", not(target_arch = "wasm32")))]
16pub(crate) fn query_wgpu_device_info(device_index: u32) -> Result<GpuDeviceInfo, MonitorError> {
17    use crate::backends::gpu::runtime;
18
19    runtime::block_on(async {
20        let instance = wgpu::Instance::default();
21
22        // Get all adapters (wgpu 27+ returns Vec directly)
23        let adapters = instance.enumerate_adapters(wgpu::Backends::all());
24
25        if adapters.is_empty() {
26            return Err(MonitorError::NoDevice);
27        }
28
29        let adapter =
30            adapters.get(device_index as usize).ok_or(MonitorError::InvalidDevice(device_index))?;
31
32        let info = adapter.get_info();
33
34        // Map wgpu backend to our backend enum
35        let backend = match info.backend {
36            wgpu::Backend::Vulkan => GpuBackend::Vulkan,
37            wgpu::Backend::Metal => GpuBackend::Metal,
38            wgpu::Backend::Dx12 => GpuBackend::Dx12,
39            wgpu::Backend::Gl => GpuBackend::OpenGl,
40            wgpu::Backend::BrowserWebGpu => GpuBackend::WebGpu,
41            wgpu::Backend::Noop => GpuBackend::Cpu,
42        };
43
44        // Map vendor ID
45        let vendor = GpuVendor::from_vendor_id(info.vendor);
46
47        // Get memory limits (rough estimate from adapter limits)
48        let limits = adapter.limits();
49        // Use max buffer size as a proxy for VRAM (not exact but gives an idea)
50        let vram_estimate = limits.max_buffer_size;
51
52        Ok(GpuDeviceInfo::new(device_index, info.name, vendor, backend)
53            .with_vram(vram_estimate)
54            .with_driver_version(format!("{:?}", info.driver_info)))
55    })
56}
57
58/// Enumerate all wgpu devices
59#[cfg(all(feature = "gpu", not(target_arch = "wasm32")))]
60pub(crate) fn enumerate_wgpu_devices() -> Result<Vec<GpuDeviceInfo>, MonitorError> {
61    use crate::backends::gpu::runtime;
62
63    runtime::block_on(async {
64        let instance = wgpu::Instance::default();
65        let adapters = instance.enumerate_adapters(wgpu::Backends::all());
66
67        if adapters.is_empty() {
68            return Err(MonitorError::NoDevice);
69        }
70
71        let mut devices = Vec::with_capacity(adapters.len());
72
73        for (idx, adapter) in adapters.iter().enumerate() {
74            let info: wgpu::AdapterInfo = adapter.get_info();
75
76            let backend = match info.backend {
77                wgpu::Backend::Vulkan => GpuBackend::Vulkan,
78                wgpu::Backend::Metal => GpuBackend::Metal,
79                wgpu::Backend::Dx12 => GpuBackend::Dx12,
80                wgpu::Backend::Gl => GpuBackend::OpenGl,
81                wgpu::Backend::BrowserWebGpu => GpuBackend::WebGpu,
82                wgpu::Backend::Noop => GpuBackend::Cpu,
83            };
84
85            let vendor = GpuVendor::from_vendor_id(info.vendor);
86            let limits = adapter.limits();
87
88            devices.push(
89                GpuDeviceInfo::new(idx as u32, info.name, vendor, backend)
90                    .with_vram(limits.max_buffer_size)
91                    .with_driver_version(format!("{:?}", info.driver_info)),
92            );
93        }
94
95        Ok(devices)
96    })
97}
98
99// ============================================================================
100// CUDA Backend Implementation (TRUENO-SPEC-010 Section 8)
101// ============================================================================
102
103/// Query device info from native CUDA via trueno-gpu
104///
105/// This provides more accurate information than wgpu including:
106/// - Actual device name (e.g., "NVIDIA GeForce RTX 4090")
107/// - Accurate VRAM total from cuDeviceTotalMem
108#[cfg(feature = "cuda-monitor")]
109pub fn query_cuda_device_info(device_index: u32) -> Result<GpuDeviceInfo, MonitorError> {
110    use trueno_gpu::CudaDeviceInfo;
111
112    let cuda_info = CudaDeviceInfo::query(device_index)
113        .map_err(|e| MonitorError::BackendInit(format!("CUDA query failed: {}", e)))?;
114
115    Ok(GpuDeviceInfo::new(
116        cuda_info.index,
117        cuda_info.name,
118        GpuVendor::Nvidia, // CUDA is NVIDIA-only
119        GpuBackend::Cuda,
120    )
121    .with_vram(cuda_info.total_memory))
122}
123
124/// Enumerate all CUDA devices via trueno-gpu
125#[cfg(feature = "cuda-monitor")]
126pub fn enumerate_cuda_devices() -> Result<Vec<GpuDeviceInfo>, MonitorError> {
127    use trueno_gpu::CudaDeviceInfo;
128
129    let cuda_devices = CudaDeviceInfo::enumerate()
130        .map_err(|e| MonitorError::BackendInit(format!("CUDA enumerate failed: {}", e)))?;
131
132    Ok(cuda_devices
133        .into_iter()
134        .map(|cuda_info| {
135            GpuDeviceInfo::new(cuda_info.index, cuda_info.name, GpuVendor::Nvidia, GpuBackend::Cuda)
136                .with_vram(cuda_info.total_memory)
137        })
138        .collect())
139}
140
141/// Query real-time CUDA memory metrics
142///
143/// Returns current free/used VRAM from cuMemGetInfo.
144#[cfg(feature = "cuda-monitor")]
145pub fn query_cuda_memory(device_index: u32) -> Result<GpuMemoryMetrics, MonitorError> {
146    use trueno_gpu::driver::CudaContext;
147    use trueno_gpu::CudaMemoryInfo;
148
149    let ctx = CudaContext::new(device_index as i32)
150        .map_err(|e| MonitorError::BackendInit(format!("CUDA context failed: {}", e)))?;
151
152    let mem = CudaMemoryInfo::query(&ctx)
153        .map_err(|e| MonitorError::QueryFailed(format!("CUDA memory query failed: {}", e)))?;
154
155    Ok(GpuMemoryMetrics::new(mem.total, mem.used(), mem.free))
156}
157
158/// Check if CUDA monitoring is available
159#[cfg(feature = "cuda-monitor")]
160#[must_use]
161pub fn cuda_monitor_available() -> bool {
162    trueno_gpu::cuda_monitoring_available()
163}
164
165/// Check if CUDA monitoring is available (stub when feature disabled)
166#[cfg(not(feature = "cuda-monitor"))]
167#[must_use]
168pub fn cuda_monitor_available() -> bool {
169    false
170}