av_denoise_core/
device.rs1use std::fmt;
25use std::str::FromStr;
26
27#[derive(Debug, Default, Clone, PartialEq, Eq)]
39pub enum Device {
40 #[default]
42 Default,
43 Discrete { index: usize },
48 Integrated { index: usize },
50 Virtual { index: usize },
52 Cpu,
55}
56
57impl FromStr for Device {
58 type Err = String;
59
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
67 let (kind, idx) = s.split_once(':').unwrap_or((s, "0"));
68 let index: usize = idx
69 .parse()
70 .map_err(|_| format!("invalid device index '{idx}' in '{s}'"))?;
71 match kind {
72 "default" => Ok(Device::Default),
73 "discrete" => Ok(Device::Discrete { index }),
74 "integrated" => Ok(Device::Integrated { index }),
75 "virtual" => Ok(Device::Virtual { index }),
76 "cpu" => Ok(Device::Cpu),
77 other => Err(format!(
78 "unknown device kind '{other}', expected default, discrete[:N], integrated[:N], virtual[:N], or cpu"
79 )),
80 }
81 }
82}
83
84impl fmt::Display for Device {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 match self {
89 Device::Default => f.write_str("default"),
90 Device::Discrete { index } => write!(f, "discrete:{index}"),
91 Device::Integrated { index } => write!(f, "integrated:{index}"),
92 Device::Virtual { index } => write!(f, "virtual:{index}"),
93 Device::Cpu => f.write_str("cpu"),
94 }
95 }
96}
97
98#[cfg(feature = "cuda")]
99impl Device {
100 pub fn to_cuda(&self) -> Result<cubecl::cuda::CudaDevice, anyhow::Error> {
101 match self {
102 Device::Default => Ok(cubecl::cuda::CudaDevice { index: 0 }),
103 Device::Discrete { index } => Ok(cubecl::cuda::CudaDevice { index: *index }),
104 other => Err(anyhow::anyhow!(
105 "device {other:?} is not supported on the CUDA runtime, use `default` or `discrete[:N]`"
106 )),
107 }
108 }
109}
110
111#[cfg(feature = "rocm")]
112impl Device {
113 pub fn to_amd(&self) -> Result<cubecl::hip::AmdDevice, anyhow::Error> {
114 match self {
115 Device::Default => Ok(cubecl::hip::AmdDevice { index: 0 }),
116 Device::Discrete { index } => Ok(cubecl::hip::AmdDevice { index: *index }),
117 other => Err(anyhow::anyhow!(
118 "device {other:?} is not supported on the ROCm runtime, use `default` or `discrete[:N]`"
119 )),
120 }
121 }
122}
123
124#[cfg(any(feature = "vulkan", feature = "metal"))]
125impl Device {
126 pub fn to_wgpu(&self) -> Result<cubecl::wgpu::WgpuDevice, anyhow::Error> {
127 use cubecl::wgpu::WgpuDevice;
128 Ok(match self {
129 Device::Default => WgpuDevice::DefaultDevice,
130 Device::Discrete { index } => WgpuDevice::DiscreteGpu(*index),
131 Device::Integrated { index } => WgpuDevice::IntegratedGpu(*index),
132 Device::Virtual { index } => WgpuDevice::VirtualGpu(*index),
133 Device::Cpu => WgpuDevice::Cpu,
134 })
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn parse_default() {
144 assert_eq!("default".parse::<Device>().unwrap(), Device::Default);
145 }
146
147 #[test]
148 fn parse_discrete_with_and_without_index() {
149 assert_eq!(
150 "discrete".parse::<Device>().unwrap(),
151 Device::Discrete { index: 0 },
152 );
153 assert_eq!(
154 "discrete:3".parse::<Device>().unwrap(),
155 Device::Discrete { index: 3 },
156 );
157 }
158
159 #[test]
160 fn parse_integrated_virtual_cpu() {
161 assert_eq!(
162 "integrated:1".parse::<Device>().unwrap(),
163 Device::Integrated { index: 1 },
164 );
165 assert_eq!(
166 "virtual:2".parse::<Device>().unwrap(),
167 Device::Virtual { index: 2 },
168 );
169 assert_eq!("cpu".parse::<Device>().unwrap(), Device::Cpu);
170 }
171
172 #[test]
173 fn parse_rejects_unknown_kind() {
174 assert!("unicorn".parse::<Device>().is_err());
175 }
176
177 #[test]
178 fn parse_rejects_non_numeric_index() {
179 assert!("discrete:abc".parse::<Device>().is_err());
180 }
181
182 #[test]
183 fn display_writes_selector_spellings() {
184 assert_eq!(Device::Default.to_string(), "default");
185 assert_eq!(Device::Discrete { index: 1 }.to_string(), "discrete:1");
186 assert_eq!(Device::Integrated { index: 0 }.to_string(), "integrated:0");
187 assert_eq!(Device::Virtual { index: 2 }.to_string(), "virtual:2");
188 assert_eq!(Device::Cpu.to_string(), "cpu");
189 }
190
191 #[test]
192 fn display_round_trips_through_from_str() {
193 let devices = [
194 Device::Default,
195 Device::Discrete { index: 3 },
196 Device::Integrated { index: 1 },
197 Device::Virtual { index: 0 },
198 Device::Cpu,
199 ];
200 for device in devices {
201 let printed = device.to_string();
202 assert_eq!(printed.parse::<Device>().unwrap(), device, "{printed}");
203 }
204 }
205
206 #[test]
207 fn default_is_default_variant() {
208 assert_eq!(Device::default(), Device::Default);
209 }
210}