nannou_wgpu/
device_map.rs

1use std::collections::HashMap;
2use std::hash::{Hash, Hasher};
3use std::ops::Deref;
4use std::sync::{Arc, Mutex, Weak};
5
6use crate as wgpu;
7
8/// A map from `RequestAdapterOptions` to active adapters.
9///
10/// Each time an adapter is requested via the `App`, it keeps track of which adapters are active.
11/// This is done in order to allow re-use of adapters and in turn re-use of logical devices and the
12/// sharing of resources between windows.
13///
14/// At the end of the application loop (after `update` and `view` have been called), adapters
15/// containing no active device connections are removed from the map.
16#[derive(Default)]
17pub struct AdapterMap {
18    map: Mutex<HashMap<AdapterMapKey, Arc<ActiveAdapter>>>,
19}
20
21/// The key into the adapter map.
22///
23/// This type is a thin wrapper around `wgpu::RequestAdapterOptions` that provides implementations
24/// of `Eq` and `Hash`.
25#[derive(Clone, Debug, Eq, Hash, PartialEq)]
26pub struct AdapterMapKey {
27    power_preference: wgpu::PowerPreference,
28}
29
30/// A single active adapter and its map of connected devices.
31pub struct ActiveAdapter {
32    adapter: wgpu::Adapter,
33    device_map: DeviceMap,
34}
35
36/// A map of actively connected devices for an adapter.
37///
38/// This is used so that windows built to target the same physical device may also target the same
39/// logical device and share the same queue. This allows for resources like textures and buffers to
40/// be shared between windows.
41///
42/// The map contains only weak handles to active adapters and cleans up unused entries at the end
43/// of each application loop.
44#[derive(Default)]
45pub struct DeviceMap {
46    map: Mutex<HashMap<DeviceMapKey, Weak<DeviceQueuePair>>>,
47}
48
49/// The key into the device map.
50///
51/// This type is a thin wrapper around `wgpu::DeviceDesriptor` that provides implementations of
52/// `Eq` and `Hash`.
53#[derive(Clone, Debug)]
54pub struct DeviceMapKey {
55    descriptor: wgpu::DeviceDescriptor<'static>,
56}
57
58/// A handle to a connected logical device and its associated queue.
59#[derive(Debug)]
60pub struct DeviceQueuePair {
61    device: wgpu::Device,
62    queue: wgpu::Queue,
63}
64
65impl AdapterMap {
66    #[cfg(not(target_os = "unknown"))]
67    /// Check for an adaptor with the given options or request one.
68    ///
69    /// First checks to see if an adapter for the given set of options is active. If so, returns a
70    /// handle to this adapter. Otherwise, requests a new adapter via `Adapter::request`.
71    ///
72    /// Returns `None` if there are no available adapters that meet the specified options.
73    pub fn get_or_request<'a, 'b>(
74        &'a self,
75        options: wgpu::RequestAdapterOptions<'b>,
76        instance: &'a wgpu::Instance,
77    ) -> Option<Arc<ActiveAdapter>> {
78        let rt = tokio::runtime::Handle::current();
79        rt.block_on(self.get_or_request_async(options, instance))
80    }
81
82    #[cfg(not(target_os = "unknown"))]
83    /// Request an adaptor with the given options.
84    ///
85    /// This will always request a new adapter and will never attempt to share an existing one. The
86    /// new adapter will take the place of the old within the map in the case that an existing
87    /// active adapter exists.
88    ///
89    /// Returns `None` if there are no available adapters that meet the specified options.
90    pub fn request<'a, 'b>(
91        &'a self,
92        options: wgpu::RequestAdapterOptions<'b>,
93        instance: &'a wgpu::Instance,
94    ) -> Option<Arc<ActiveAdapter>> {
95        let rt = tokio::runtime::Handle::current();
96        rt.block_on(self.request_async(options, instance))
97    }
98
99    /// The async implementation of `get_or_request`.
100    pub async fn get_or_request_async<'a, 'b>(
101        &'a self,
102        options: wgpu::RequestAdapterOptions<'b>,
103        instance: &'a wgpu::Instance,
104    ) -> Option<Arc<ActiveAdapter>> {
105        let power_preference = options.power_preference;
106        let key = AdapterMapKey { power_preference };
107        let mut map = self
108            .map
109            .lock()
110            .expect("failed to acquire `AdapterMap` lock");
111        if let Some(adapter) = map.get(&key) {
112            return Some(adapter.clone());
113        }
114        if let Some(adapter) = instance.request_adapter(&options).await {
115            let device_map = Default::default();
116            let adapter = Arc::new(ActiveAdapter {
117                adapter,
118                device_map,
119            });
120            return Some(map.entry(key).or_insert(adapter).clone());
121        }
122        None
123    }
124
125    /// The async implementation of `request`.
126    pub async fn request_async<'a, 'b>(
127        &'a self,
128        options: wgpu::RequestAdapterOptions<'b>,
129        instance: &'b wgpu::Instance,
130    ) -> Option<Arc<ActiveAdapter>> {
131        let adapter = instance.request_adapter(&options).await?;
132        let device_map = Default::default();
133        let adapter = Arc::new(ActiveAdapter {
134            adapter,
135            device_map,
136        });
137        let power_preference = options.power_preference;
138        let key = AdapterMapKey { power_preference };
139        let mut map = self
140            .map
141            .lock()
142            .expect("failed to acquire `AdapterMap` lock");
143        map.insert(key, adapter.clone());
144        Some(adapter)
145    }
146
147    /// Clear all adapters that currently have no connected devices.
148    ///
149    /// First clears all devices that no longer have any external references.
150    pub fn clear_inactive_adapters_and_devices(&self) {
151        let mut map = self
152            .map
153            .lock()
154            .expect("failed to acquire `AdapterMap` lock");
155        map.retain(|_, adapter| {
156            adapter.clear_inactive_devices();
157            adapter.device_count() > 0
158        });
159    }
160
161    /// Poll all devices within all active adapters.
162    pub(crate) fn _poll_all_devices(&self, maintain: wgpu::Maintain) {
163        let map = self
164            .map
165            .lock()
166            .expect("failed to acquire `AdapterMap` lock");
167        for adapter in map.values() {
168            adapter._poll_all_devices(maintain.clone()); // TODO: clone?
169        }
170    }
171}
172
173impl ActiveAdapter {
174    #[cfg(not(target_os = "unknown"))]
175    /// Check for a device with the given descriptor or request one.
176    ///
177    /// First checks for a connected device that matches the given descriptor. If one exists, it is
178    /// returned. Otherwise, a new device connection is requested via `Adapter::request_device`.
179    pub fn get_or_request_device(
180        &self,
181        descriptor: wgpu::DeviceDescriptor<'static>,
182    ) -> Arc<DeviceQueuePair> {
183        let rt = tokio::runtime::Handle::current();
184        rt.block_on(self.get_or_request_device_async(descriptor))
185    }
186
187    #[cfg(not(target_os = "unknown"))]
188    /// Request a device with the given descriptor.
189    ///
190    /// This will always request a new device connection and will never attempt to share an
191    /// existing one. The new device will take the place of the old within the map in the case that
192    /// an existing connected device exists.
193    pub fn request_device(
194        &self,
195        descriptor: wgpu::DeviceDescriptor<'static>,
196    ) -> Arc<DeviceQueuePair> {
197        let rt = tokio::runtime::Handle::current();
198        rt.block_on(self.request_device_async(descriptor))
199    }
200
201    /// Check for a device with the given descriptor or request one.
202    ///
203    /// First checks for a connected device that matches the given descriptor. If one exists, it is
204    /// returned. Otherwise, a new device connection is requested via `Adapter::request_device`.
205    pub async fn get_or_request_device_async(
206        &self,
207        descriptor: wgpu::DeviceDescriptor<'static>,
208    ) -> Arc<DeviceQueuePair> {
209        let key = DeviceMapKey { descriptor };
210        let mut map = self
211            .device_map
212            .map
213            .lock()
214            .expect("failed to acquire `AdapterMap` lock");
215        if let Some(device_ref) = map.get(&key) {
216            if let Some(device) = device_ref.upgrade() {
217                return device;
218            }
219        }
220        let (device, queue) = self
221            .adapter
222            .request_device(&key.descriptor, None)
223            .await
224            .expect("could not get or request device");
225        let device = Arc::new(DeviceQueuePair { device, queue });
226        map.insert(key, Arc::downgrade(&device));
227        device
228    }
229
230    /// Request a device with the given descriptor.
231    ///
232    /// This will always request a new device connection and will never attempt to share an
233    /// existing one. The new device will take the place of the old within the map in the case that
234    /// an existing connected device exists.
235    pub async fn request_device_async(
236        &self,
237        descriptor: wgpu::DeviceDescriptor<'static>,
238    ) -> Arc<DeviceQueuePair> {
239        let (device, queue) = self
240            .adapter
241            .request_device(&descriptor, None)
242            .await
243            .expect("could not request device async");
244        let device = Arc::new(DeviceQueuePair { device, queue });
245        let key = DeviceMapKey { descriptor };
246        let mut map = self
247            .device_map
248            .map
249            .lock()
250            .expect("failed to acquire `DeviceMap` lock");
251        map.insert(key, Arc::downgrade(&device));
252        device
253    }
254
255    /// A count of devices that are currently active.
256    pub fn device_count(&self) -> usize {
257        let map = self
258            .device_map
259            .map
260            .lock()
261            .expect("failed to acquire `DeviceMap` lock");
262        map.len()
263    }
264
265    /// Clear all device queue pairs that have been dropped.
266    pub fn clear_inactive_devices(&self) {
267        let mut map = self
268            .device_map
269            .map
270            .lock()
271            .expect("failed to acquire `DeviceMap` lock");
272        map.retain(|_, pair| pair.upgrade().is_some());
273    }
274
275    /// Poll all of the active devices within the map.
276    fn _poll_all_devices(&self, maintain: wgpu::Maintain) {
277        let map = self
278            .device_map
279            .map
280            .lock()
281            .expect("failed to acquire `DeviceMap` lock");
282        for weak in map.values() {
283            if let Some(pair) = weak.upgrade() {
284                pair.device().poll(maintain.clone()); // TODO: clone?
285            }
286        }
287    }
288}
289
290impl DeviceQueuePair {
291    /// A reference to the inner `wgpu::Device`.
292    pub fn device(&self) -> &wgpu::Device {
293        &self.device
294    }
295
296    /// A reference to the inner `wgpu::Queue`.
297    ///
298    /// The queue is guarded by a `Mutex` in order to synchronise submissions of command buffers in
299    /// cases that the queue is shared between more than one window.
300    pub fn queue(&self) -> &wgpu::Queue {
301        &self.queue
302    }
303}
304
305impl Deref for ActiveAdapter {
306    type Target = wgpu::Adapter;
307    fn deref(&self) -> &Self::Target {
308        &self.adapter
309    }
310}
311
312impl Hash for DeviceMapKey {
313    fn hash<H: Hasher>(&self, state: &mut H) {
314        hash_device_descriptor(&self.descriptor, state);
315    }
316}
317
318impl PartialEq for DeviceMapKey {
319    fn eq(&self, other: &Self) -> bool {
320        eq_device_descriptor(&self.descriptor, &other.descriptor)
321    }
322}
323
324impl Eq for DeviceMapKey {}
325
326// NOTE: This should be updated as fields are added to the `wgpu::DeviceDescriptor` type.
327fn eq_device_descriptor(
328    a: &wgpu::DeviceDescriptor<'static>,
329    b: &wgpu::DeviceDescriptor<'static>,
330) -> bool {
331    a.label == b.label && a.features == b.features && a.limits == b.limits
332}
333
334// NOTE: This should be updated as fields are added to the `wgpu::DeviceDescriptor` type.
335fn hash_device_descriptor<H>(desc: &wgpu::DeviceDescriptor<'static>, state: &mut H)
336where
337    H: Hasher,
338{
339    desc.label.hash(state);
340    desc.features.hash(state);
341    desc.limits.hash(state);
342}