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