Skip to main content

oximedia_gpu/
device.rs

1//! GPU device management and enumeration
2
3use crate::{GpuError, Result};
4use std::sync::Arc;
5use wgpu::{
6    Adapter, Device, DeviceDescriptor, Features, Instance, Limits, PowerPreference, Queue,
7    RequestAdapterOptions,
8};
9
10/// Information about a GPU device
11#[derive(Debug, Clone)]
12pub struct GpuDeviceInfo {
13    /// Device name
14    pub name: String,
15    /// Vendor ID
16    pub vendor: u32,
17    /// Device ID
18    pub device: u32,
19    /// Device type (discrete, integrated, virtual, cpu, unknown)
20    pub device_type: String,
21    /// Backend being used (Vulkan, Metal, DX12, etc.)
22    pub backend: String,
23}
24
25/// GPU device wrapper
26///
27/// This structure manages the WGPU device and queue, providing a safe
28/// interface for GPU operations.
29pub 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    /// Create a new GPU device
39    ///
40    /// # Arguments
41    ///
42    /// * `device_index` - Optional device index for multi-GPU selection
43    ///
44    /// # Errors
45    ///
46    /// Returns an error if no suitable adapter is found or device request fails.
47    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    /// List all available GPU devices
64    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    /// Get device information
72    #[must_use]
73    pub fn info(&self) -> &GpuDeviceInfo {
74        &self.info
75    }
76
77    /// Get the WGPU device
78    #[must_use]
79    pub fn device(&self) -> &Arc<Device> {
80        &self.device
81    }
82
83    /// Get the WGPU queue
84    #[must_use]
85    pub fn queue(&self) -> &Arc<Queue> {
86        &self.queue
87    }
88
89    /// Wait for all GPU operations to complete
90    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            // Select high-performance adapter by default
107            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}