1use crate::{GpuError, Result};
4use std::sync::Arc;
5use wgpu::{
6 Adapter, Device, DeviceDescriptor, Features, Instance, Limits, PowerPreference, Queue,
7 RequestAdapterOptions,
8};
9
10#[derive(Debug, Clone)]
12pub struct GpuDeviceInfo {
13 pub name: String,
15 pub vendor: u32,
17 pub device: u32,
19 pub device_type: String,
21 pub backend: String,
23}
24
25pub struct GpuDevice {
30 device: Arc<Device>,
31 queue: Arc<Queue>,
32 info: GpuDeviceInfo,
33 #[allow(dead_code)]
34 adapter: Adapter,
35 pub is_fallback: bool,
38}
39
40impl GpuDevice {
41 pub fn new(device_index: Option<usize>) -> Result<Self> {
51 let instance = Self::create_instance();
52 let adapter = pollster::block_on(Self::select_adapter(&instance, device_index))?;
53
54 let info = Self::adapter_info(&adapter);
55
56 let (device, queue) = pollster::block_on(Self::request_device(&adapter))?;
57
58 Ok(Self {
59 device: Arc::new(device),
60 queue: Arc::new(queue),
61 info,
62 adapter,
63 is_fallback: false,
64 })
65 }
66
67 pub fn new_fallback() -> Result<Self> {
80 let instance = Self::create_instance();
81
82 let maybe_adapter = pollster::block_on(instance.request_adapter(&RequestAdapterOptions {
85 power_preference: PowerPreference::None,
86 compatible_surface: None,
87 force_fallback_adapter: true,
88 }));
89
90 if let Some(adapter) = maybe_adapter {
91 let info = Self::adapter_info(&adapter);
92 if let Ok((device, queue)) = pollster::block_on(Self::request_device(&adapter)) {
93 return Ok(Self {
94 device: Arc::new(device),
95 queue: Arc::new(queue),
96 info,
97 adapter,
98 is_fallback: true,
99 });
100 }
101 }
102
103 Self::make_null_device().ok_or(GpuError::NoAdapter)
105 }
106
107 fn make_null_device() -> Option<Self> {
114 let null_info = GpuDeviceInfo {
115 name: "CPU Null Device".to_string(),
116 vendor: 0,
117 device: 0,
118 device_type: "cpu".to_string(),
119 backend: "Null".to_string(),
120 };
121
122 fn try_adapter(adapter: Adapter, info: GpuDeviceInfo) -> Option<GpuDevice> {
125 match pollster::block_on(GpuDevice::request_device(&adapter)) {
126 Ok((device, queue)) => Some(GpuDevice {
127 device: Arc::new(device),
128 queue: Arc::new(queue),
129 info,
130 adapter,
131 is_fallback: true,
132 }),
133 Err(_) => None,
134 }
135 }
136
137 let gl_instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
139 backends: wgpu::Backends::GL,
140 ..Default::default()
141 });
142 if let Some(adapter) =
143 pollster::block_on(gl_instance.request_adapter(&RequestAdapterOptions {
144 power_preference: PowerPreference::None,
145 compatible_surface: None,
146 force_fallback_adapter: true,
147 }))
148 {
149 let info = Self::adapter_info(&adapter);
150 if let Some(dev) = try_adapter(adapter, info) {
151 return Some(dev);
152 }
153 }
154
155 let adapters = gl_instance.enumerate_adapters(wgpu::Backends::all());
157 for adapter in adapters {
158 let info = Self::adapter_info(&adapter);
159 if let Some(dev) = try_adapter(adapter, info) {
160 return Some(dev);
161 }
162 }
163
164 let default_instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
166 if let Some(adapter) =
167 pollster::block_on(default_instance.request_adapter(&RequestAdapterOptions {
168 power_preference: PowerPreference::None,
169 compatible_surface: None,
170 force_fallback_adapter: true,
171 }))
172 {
173 let info = Self::adapter_info(&adapter);
174 if let Some(dev) = try_adapter(adapter, info) {
175 return Some(dev);
176 }
177 }
178
179 let adapters = default_instance.enumerate_adapters(wgpu::Backends::all());
181 for adapter in adapters {
182 let info = Self::adapter_info(&adapter);
183 if let Some(dev) = try_adapter(adapter, info) {
184 return Some(dev);
185 }
186 }
187
188 let _ = null_info;
190 None
191 }
192
193 pub fn list_devices() -> Result<Vec<GpuDeviceInfo>> {
195 let instance = Self::create_instance();
196
197 #[cfg(not(target_arch = "wasm32"))]
198 {
199 let adapters = instance.enumerate_adapters(wgpu::Backends::all());
200 Ok(adapters.iter().map(Self::adapter_info).collect())
201 }
202
203 #[cfg(target_arch = "wasm32")]
204 {
205 let adapter = pollster::block_on(instance.request_adapter(&RequestAdapterOptions {
207 power_preference: PowerPreference::HighPerformance,
208 compatible_surface: None,
209 force_fallback_adapter: false,
210 }));
211 match adapter {
212 Some(a) => Ok(vec![Self::adapter_info(&a)]),
213 None => Ok(Vec::new()),
214 }
215 }
216 }
217
218 #[must_use]
220 pub fn info(&self) -> &GpuDeviceInfo {
221 &self.info
222 }
223
224 #[must_use]
226 pub fn device(&self) -> &Arc<Device> {
227 &self.device
228 }
229
230 #[must_use]
232 pub fn queue(&self) -> &Arc<Queue> {
233 &self.queue
234 }
235
236 pub fn wait(&self) {
238 self.device.poll(wgpu::Maintain::Wait);
239 }
240
241 fn create_instance() -> Instance {
242 Instance::new(&wgpu::InstanceDescriptor {
243 backends: wgpu::Backends::all(),
244 ..Default::default()
245 })
246 }
247
248 async fn select_adapter(instance: &Instance, device_index: Option<usize>) -> Result<Adapter> {
249 if let Some(index) = device_index {
250 #[cfg(not(target_arch = "wasm32"))]
251 {
252 let adapters = instance.enumerate_adapters(wgpu::Backends::all());
253 return adapters.into_iter().nth(index).ok_or(GpuError::NoAdapter);
254 }
255
256 #[cfg(target_arch = "wasm32")]
257 {
258 if index != 0 {
260 return Err(GpuError::NoAdapter);
261 }
262 return instance
263 .request_adapter(&RequestAdapterOptions {
264 power_preference: PowerPreference::HighPerformance,
265 compatible_surface: None,
266 force_fallback_adapter: false,
267 })
268 .await
269 .ok_or(GpuError::NoAdapter);
270 }
271 } else {
272 instance
274 .request_adapter(&RequestAdapterOptions {
275 power_preference: PowerPreference::HighPerformance,
276 compatible_surface: None,
277 force_fallback_adapter: false,
278 })
279 .await
280 .ok_or(GpuError::NoAdapter)
281 }
282 }
283
284 async fn request_device(adapter: &Adapter) -> Result<(Device, Queue)> {
285 adapter
286 .request_device(
287 &DeviceDescriptor {
288 label: Some("OxiMedia GPU Device"),
289 required_features: Features::empty(),
290 required_limits: Limits::default(),
291 memory_hints: wgpu::MemoryHints::default(),
292 },
293 None,
294 )
295 .await
296 .map_err(|e| GpuError::DeviceRequest(e.to_string()))
297 }
298
299 fn adapter_info(adapter: &Adapter) -> GpuDeviceInfo {
300 let info = adapter.get_info();
301
302 let device_type = match info.device_type {
303 wgpu::DeviceType::DiscreteGpu => "discrete",
304 wgpu::DeviceType::IntegratedGpu => "integrated",
305 wgpu::DeviceType::VirtualGpu => "virtual",
306 wgpu::DeviceType::Cpu => "cpu",
307 wgpu::DeviceType::Other => "unknown",
308 };
309
310 let backend = match info.backend {
311 wgpu::Backend::Vulkan => "Vulkan",
312 wgpu::Backend::Metal => "Metal",
313 wgpu::Backend::Dx12 => "DirectX 12",
314 wgpu::Backend::Gl => "OpenGL",
315 wgpu::Backend::BrowserWebGpu => "WebGPU",
316 _ => "Unknown",
317 };
318
319 GpuDeviceInfo {
320 name: info.name,
321 vendor: info.vendor,
322 device: info.device,
323 device_type: device_type.to_string(),
324 backend: backend.to_string(),
325 }
326 }
327}
328
329impl std::fmt::Debug for GpuDevice {
330 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 f.debug_struct("GpuDevice")
332 .field("info", &self.info)
333 .field("is_fallback", &self.is_fallback)
334 .finish()
335 }
336}