1use crate::video::error::{VideoError, VideoResult};
7use serde::{Deserialize, Serialize};
8use std::fmt;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
12#[serde(rename_all = "lowercase")]
13pub enum GpuSelection {
14 #[default]
16 Auto,
17 Integrated,
19 Discrete,
21 Named(String),
23}
24
25impl fmt::Display for GpuSelection {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 match self {
28 Self::Auto => write!(f, "auto"),
29 Self::Integrated => write!(f, "integrated"),
30 Self::Discrete => write!(f, "discrete"),
31 Self::Named(name) => write!(f, "{}", name),
32 }
33 }
34}
35
36#[derive(Debug, Clone)]
38pub struct AdapterInfo {
39 pub name: String,
40 pub backend: wgpu::Backend,
41 pub device_type: wgpu::DeviceType,
42 pub driver: String,
43 pub driver_info: String,
44}
45
46impl AdapterInfo {
47 pub fn is_integrated(&self) -> bool {
49 self.device_type == wgpu::DeviceType::IntegratedGpu
50 }
51
52 pub fn is_discrete(&self) -> bool {
54 self.device_type == wgpu::DeviceType::DiscreteGpu
55 }
56}
57
58impl fmt::Display for AdapterInfo {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(
61 f,
62 "{} ({:?}, {:?}) - {}",
63 self.name, self.device_type, self.backend, self.driver
64 )
65 }
66}
67
68pub async fn detect_adapters(instance: &wgpu::Instance) -> Vec<AdapterInfo> {
70 let mut adapters = Vec::new();
71
72 for adapter in instance.enumerate_adapters(wgpu::Backends::all()) {
73 let info = adapter.get_info();
74 adapters.push(AdapterInfo {
75 name: info.name.clone(),
76 backend: info.backend,
77 device_type: info.device_type,
78 driver: info.driver.clone(),
79 driver_info: info.driver_info.clone(),
80 });
81 }
82
83 tracing::info!("Detected {} GPU adapter(s)", adapters.len());
84 for (i, adapter) in adapters.iter().enumerate() {
85 tracing::info!(" [{}] {}", i, adapter);
86 }
87
88 adapters
89}
90
91pub async fn select_adapter(
101 instance: &wgpu::Instance,
102 preference: &GpuSelection,
103) -> VideoResult<wgpu::Adapter> {
104 let adapters = detect_adapters(instance).await;
105
106 if adapters.is_empty() {
107 return Err(VideoError::AdapterNotFound(
108 "No GPU adapters detected".to_string(),
109 ));
110 }
111
112 let selected_info = match preference {
113 GpuSelection::Auto => {
114 adapters
116 .iter()
117 .find(|a| a.is_integrated())
118 .or_else(|| adapters.first())
119 }
120 GpuSelection::Integrated => adapters.iter().find(|a| a.is_integrated()),
121 GpuSelection::Discrete => adapters.iter().find(|a| a.is_discrete()),
122 GpuSelection::Named(name) => adapters.iter().find(|a| a.name.contains(name)),
123 };
124
125 let selected_info = selected_info.ok_or_else(|| {
126 VideoError::AdapterNotFound(format!("No adapter matching preference: {}", preference))
127 })?;
128
129 tracing::info!("Selected GPU adapter: {}", selected_info);
130
131 let adapter = instance
133 .request_adapter(&wgpu::RequestAdapterOptions {
134 power_preference: match preference {
135 GpuSelection::Integrated => wgpu::PowerPreference::LowPower,
136 GpuSelection::Discrete => wgpu::PowerPreference::HighPerformance,
137 _ => wgpu::PowerPreference::LowPower,
138 },
139 compatible_surface: None,
140 force_fallback_adapter: false,
141 })
142 .await
143 .ok_or_else(|| {
144 VideoError::AdapterNotFound(format!("Failed to request adapter for: {}", preference))
145 })?;
146
147 Ok(adapter)
148}
149
150pub fn adapter_diagnostics(adapter: &wgpu::Adapter) -> String {
152 let info = adapter.get_info();
153 format!(
154 "GPU: {} ({:?})\nBackend: {:?}\nDriver: {}\nDriver Info: {}",
155 info.name, info.device_type, info.backend, info.driver, info.driver_info
156 )
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn test_gpu_selection_display() {
165 assert_eq!(GpuSelection::Auto.to_string(), "auto");
166 assert_eq!(GpuSelection::Integrated.to_string(), "integrated");
167 assert_eq!(GpuSelection::Discrete.to_string(), "discrete");
168 assert_eq!(
169 GpuSelection::Named("NVIDIA".to_string()).to_string(),
170 "NVIDIA"
171 );
172 }
173
174 #[test]
175 fn test_gpu_selection_default() {
176 assert_eq!(GpuSelection::default(), GpuSelection::Auto);
177 }
178}