trueno/backends/gpu/
pool.rs1use super::GpuDevice;
22
23pub struct GpuDevicePool {
28 devices: Vec<GpuDevice>,
29 indices: Vec<u32>,
30}
31
32impl GpuDevicePool {
33 #[cfg(all(feature = "gpu", not(target_arch = "wasm32")))]
37 pub fn all() -> Result<Self, String> {
38 use super::runtime;
39 runtime::block_on(Self::all_async())
40 }
41
42 pub async fn all_async() -> Result<Self, String> {
44 let instance = super::device::shared_instance();
47 let adapters = instance.enumerate_adapters(super::device::gpu_backends());
50
51 if adapters.is_empty() {
52 return Err("No GPU adapters found".to_string());
53 }
54
55 let gpu_adapters: Vec<(usize, _)> = adapters
57 .into_iter()
58 .enumerate()
59 .filter(|(_, adapter)| adapter.get_info().backend != wgpu::Backend::Noop)
60 .collect();
61
62 if gpu_adapters.is_empty() {
63 return Err("No non-CPU GPU adapters found".to_string());
64 }
65
66 let mut devices = Vec::with_capacity(gpu_adapters.len());
67 let mut indices = Vec::with_capacity(gpu_adapters.len());
68
69 for (idx, adapter) in gpu_adapters {
70 let mut limits = wgpu::Limits::default();
71 limits.max_buffer_size = adapter.limits().max_buffer_size;
72 limits.max_storage_buffer_binding_size =
73 adapter.limits().max_storage_buffer_binding_size;
74
75 let (device, queue) = adapter
76 .request_device(&wgpu::DeviceDescriptor {
77 label: Some(&format!("Trueno GPU Device [{}]", idx)),
78 required_features: wgpu::Features::empty(),
79 required_limits: limits,
80 memory_hints: wgpu::MemoryHints::Performance,
81 experimental_features: Default::default(),
82 trace: Default::default(),
83 })
84 .await
85 .map_err(|e| format!("Failed to create device at index {}: {}", idx, e))?;
86
87 devices.push(GpuDevice { device, queue });
88 indices.push(idx as u32);
89 }
90
91 Ok(Self { devices, indices })
92 }
93
94 #[cfg(all(feature = "gpu", not(target_arch = "wasm32")))]
96 pub fn with_indices(adapter_indices: &[u32]) -> Result<Self, String> {
97 use super::runtime;
98 runtime::block_on(Self::with_indices_async(adapter_indices))
99 }
100
101 pub async fn with_indices_async(adapter_indices: &[u32]) -> Result<Self, String> {
103 if adapter_indices.is_empty() {
104 return Err("No adapter indices specified".to_string());
105 }
106
107 let mut devices = Vec::with_capacity(adapter_indices.len());
108 let mut indices = Vec::with_capacity(adapter_indices.len());
109
110 for &idx in adapter_indices {
111 let device = GpuDevice::new_with_adapter_index_async(idx).await?;
112 devices.push(device);
113 indices.push(idx);
114 }
115
116 Ok(Self { devices, indices })
117 }
118
119 #[must_use]
121 pub fn len(&self) -> usize {
122 self.devices.len()
123 }
124
125 #[must_use]
127 pub fn is_empty(&self) -> bool {
128 self.devices.is_empty()
129 }
130
131 #[must_use]
133 pub fn get(&self, pool_index: usize) -> Option<&GpuDevice> {
134 self.devices.get(pool_index)
135 }
136
137 #[must_use]
139 pub fn adapter_index(&self, pool_index: usize) -> Option<u32> {
140 self.indices.get(pool_index).copied()
141 }
142
143 pub fn iter(&self) -> impl Iterator<Item = (u32, &GpuDevice)> {
145 self.indices.iter().copied().zip(self.devices.iter())
146 }
147
148 pub fn into_devices(self) -> Vec<GpuDevice> {
150 self.devices
151 }
152}
153
154#[cfg(all(test, feature = "gpu", not(target_arch = "wasm32")))]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_pool_len_matches_devices() {
160 if !GpuDevice::is_available() {
161 eprintln!("GPU not available, skipping pool test");
162 return;
163 }
164
165 let pool = GpuDevicePool::all();
166 match pool {
167 Ok(p) => {
168 assert!(!p.is_empty());
169 assert!(p.len() > 0);
170 assert!(p.get(0).is_some());
171 assert!(p.adapter_index(0).is_some());
172 }
173 Err(e) => {
174 eprintln!("Pool creation failed (expected on CPU-only): {}", e);
175 }
176 }
177 }
178}