av_denoise_core/
enumerate.rs1use cubecl::device::DeviceId;
2use cubecl::prelude::*;
3
4use crate::accelerate::Accelerator;
5use crate::device::Device;
6use crate::probe::open_client;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct BackendDevices {
11 pub accelerator: Accelerator,
13 pub available: bool,
19 pub devices: Vec<Device>,
23}
24
25pub fn enumerate_devices(enable: &[Accelerator]) -> Vec<BackendDevices> {
31 enable
32 .iter()
33 .map(|accelerator| match accelerator {
34 #[cfg(feature = "cuda")]
35 Accelerator::Cuda => match Device::Default.to_cuda() {
36 Ok(dev) => query_runtime::<cubecl::cuda::CudaRuntime>(*accelerator, &dev),
37 Err(_) => unavailable(*accelerator),
38 },
39 #[cfg(feature = "rocm")]
40 Accelerator::Rocm => match Device::Default.to_amd() {
41 Ok(dev) => query_runtime::<cubecl::hip::HipRuntime>(*accelerator, &dev),
42 Err(_) => unavailable(*accelerator),
43 },
44 #[cfg(feature = "vulkan")]
45 Accelerator::Vulkan => match Device::Default.to_wgpu() {
46 Ok(dev) => query_runtime::<cubecl::wgpu::WgpuRuntime>(*accelerator, &dev),
47 Err(_) => unavailable(*accelerator),
48 },
49 #[cfg(feature = "metal")]
50 Accelerator::Metal => match Device::Default.to_wgpu() {
51 Ok(dev) => query_runtime::<cubecl::wgpu::WgpuRuntime>(*accelerator, &dev),
52 Err(_) => unavailable(*accelerator),
53 },
54 #[cfg(docsrs)]
58 #[allow(unreachable_patterns)]
59 _ => unreachable!(),
60 })
61 .collect()
62}
63
64fn unavailable(accelerator: Accelerator) -> BackendDevices {
65 BackendDevices {
66 accelerator,
67 available: false,
68 devices: Vec::new(),
69 }
70}
71
72fn query_runtime<R: Runtime>(accelerator: Accelerator, device: &R::Device) -> BackendDevices {
78 let Some(client) = open_client::<R>(accelerator, device) else {
79 return unavailable(accelerator);
80 };
81
82 let mut devices: Vec<Device> = Vec::new();
89 for type_id in 0..=3 {
90 for id in client.enumerate_devices(type_id) {
91 if let Some(device) = to_device(id)
92 && !devices.contains(&device)
93 {
94 devices.push(device);
95 }
96 }
97 }
98
99 BackendDevices {
100 accelerator,
101 available: true,
102 devices,
103 }
104}
105
106fn to_device(id: DeviceId) -> Option<Device> {
111 let index = id.index_id as usize;
112 match id.type_id {
113 0 => Some(Device::Discrete { index }),
114 1 => Some(Device::Integrated { index }),
115 2 => Some(Device::Virtual { index }),
116 3 => Some(Device::Cpu),
117 _ => None,
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn device_kinds_map_from_type_ids() {
127 assert_eq!(
128 to_device(DeviceId::new(0, 1)),
129 Some(Device::Discrete { index: 1 }),
130 );
131 assert_eq!(
132 to_device(DeviceId::new(1, 0)),
133 Some(Device::Integrated { index: 0 }),
134 );
135 assert_eq!(to_device(DeviceId::new(2, 2)), Some(Device::Virtual { index: 2 }),);
136 assert_eq!(to_device(DeviceId::new(3, 0)), Some(Device::Cpu));
137 }
138
139 #[test]
140 fn unknown_type_ids_are_skipped() {
141 assert_eq!(to_device(DeviceId::new(4, 0)), None);
142 }
143
144 #[test]
145 fn no_backends_lists_nothing() {
146 assert!(enumerate_devices(&[]).is_empty());
147 }
148
149 #[cfg(feature = "vulkan")]
150 #[test]
151 fn vulkan_reports_at_least_one_device() {
152 let reported = enumerate_devices(&[Accelerator::Vulkan]);
153 assert_eq!(reported.len(), 1);
154
155 let vulkan = &reported[0];
156 assert_eq!(vulkan.accelerator, Accelerator::Vulkan);
157 assert!(vulkan.available, "the vulkan backend did not start");
158 assert!(
159 !vulkan.devices.is_empty(),
160 "the vulkan backend started but listed no devices",
161 );
162
163 let mut unique = vulkan.devices.clone();
164 unique.dedup();
165 assert_eq!(
166 unique.len(),
167 vulkan.devices.len(),
168 "a device was listed more than once: {:?}",
169 vulkan.devices,
170 );
171 }
172}