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}
36
37impl GpuDevice {
38 pub fn new(device_index: Option<usize>) -> Result<Self> {
48 let instance = Self::create_instance();
49 let adapter = pollster::block_on(Self::select_adapter(&instance, device_index))?;
50
51 let info = Self::adapter_info(&adapter);
52
53 let (device, queue) = pollster::block_on(Self::request_device(&adapter))?;
54
55 Ok(Self {
56 device: Arc::new(device),
57 queue: Arc::new(queue),
58 info,
59 adapter,
60 })
61 }
62
63 pub fn list_devices() -> Result<Vec<GpuDeviceInfo>> {
65 let instance = Self::create_instance();
66 let adapters = instance.enumerate_adapters(wgpu::Backends::all());
67
68 Ok(adapters.iter().map(Self::adapter_info).collect())
69 }
70
71 #[must_use]
73 pub fn info(&self) -> &GpuDeviceInfo {
74 &self.info
75 }
76
77 #[must_use]
79 pub fn device(&self) -> &Arc<Device> {
80 &self.device
81 }
82
83 #[must_use]
85 pub fn queue(&self) -> &Arc<Queue> {
86 &self.queue
87 }
88
89 pub fn wait(&self) {
91 self.device.poll(wgpu::Maintain::Wait);
92 }
93
94 fn create_instance() -> Instance {
95 Instance::new(&wgpu::InstanceDescriptor {
96 backends: wgpu::Backends::all(),
97 ..Default::default()
98 })
99 }
100
101 async fn select_adapter(instance: &Instance, device_index: Option<usize>) -> Result<Adapter> {
102 if let Some(index) = device_index {
103 let adapters = instance.enumerate_adapters(wgpu::Backends::all());
104 adapters.into_iter().nth(index).ok_or(GpuError::NoAdapter)
105 } else {
106 instance
108 .request_adapter(&RequestAdapterOptions {
109 power_preference: PowerPreference::HighPerformance,
110 compatible_surface: None,
111 force_fallback_adapter: false,
112 })
113 .await
114 .ok_or(GpuError::NoAdapter)
115 }
116 }
117
118 async fn request_device(adapter: &Adapter) -> Result<(Device, Queue)> {
119 adapter
120 .request_device(
121 &DeviceDescriptor {
122 label: Some("OxiMedia GPU Device"),
123 required_features: Features::empty(),
124 required_limits: Limits::default(),
125 memory_hints: wgpu::MemoryHints::default(),
126 },
127 None,
128 )
129 .await
130 .map_err(|e| GpuError::DeviceRequest(e.to_string()))
131 }
132
133 fn adapter_info(adapter: &Adapter) -> GpuDeviceInfo {
134 let info = adapter.get_info();
135
136 let device_type = match info.device_type {
137 wgpu::DeviceType::DiscreteGpu => "discrete",
138 wgpu::DeviceType::IntegratedGpu => "integrated",
139 wgpu::DeviceType::VirtualGpu => "virtual",
140 wgpu::DeviceType::Cpu => "cpu",
141 wgpu::DeviceType::Other => "unknown",
142 };
143
144 let backend = match info.backend {
145 wgpu::Backend::Vulkan => "Vulkan",
146 wgpu::Backend::Metal => "Metal",
147 wgpu::Backend::Dx12 => "DirectX 12",
148 wgpu::Backend::Gl => "OpenGL",
149 wgpu::Backend::BrowserWebGpu => "WebGPU",
150 _ => "Unknown",
151 };
152
153 GpuDeviceInfo {
154 name: info.name,
155 vendor: info.vendor,
156 device: info.device,
157 device_type: device_type.to_string(),
158 backend: backend.to_string(),
159 }
160 }
161}
162
163impl std::fmt::Debug for GpuDevice {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 f.debug_struct("GpuDevice")
166 .field("info", &self.info)
167 .finish()
168 }
169}