wgpu_core/
as_hal.rs

1use core::{mem::ManuallyDrop, ops::Deref};
2
3use alloc::sync::Arc;
4use hal::DynResource;
5
6use crate::{
7    device::Device,
8    global::Global,
9    hal_api::HalApi,
10    id::{
11        AdapterId, BlasId, BufferId, CommandEncoderId, DeviceId, QueueId, SurfaceId, TextureId,
12        TextureViewId, TlasId,
13    },
14    lock::{RankData, RwLockReadGuard},
15    resource::RawResourceAccess,
16    snatch::SnatchGuard,
17};
18
19/// A guard which holds alive a wgpu-core resource and dereferences to the Hal type.
20struct SimpleResourceGuard<Resource, HalType> {
21    _guard: Resource,
22    ptr: *const HalType,
23}
24
25impl<Resource, HalType> SimpleResourceGuard<Resource, HalType> {
26    /// Creates a new guard from a resource, using a callback to derive the Hal type.
27    pub fn new<C>(guard: Resource, callback: C) -> Option<Self>
28    where
29        C: Fn(&Resource) -> Option<&HalType>,
30    {
31        // Derive the hal type from the resource and coerce it to a pointer.
32        let ptr: *const HalType = callback(&guard)?;
33
34        Some(Self { _guard: guard, ptr })
35    }
36}
37
38impl<Resource, HalType> Deref for SimpleResourceGuard<Resource, HalType> {
39    type Target = HalType;
40
41    fn deref(&self) -> &Self::Target {
42        // SAFETY: The pointer is guaranteed to be valid as the original resource is
43        // still alive and this guard cannot be used with snatchable resources.
44        unsafe { &*self.ptr }
45    }
46}
47
48unsafe impl<Resource, HalType> Send for SimpleResourceGuard<Resource, HalType>
49where
50    Resource: Send,
51    HalType: Send,
52{
53}
54unsafe impl<Resource, HalType> Sync for SimpleResourceGuard<Resource, HalType>
55where
56    Resource: Sync,
57    HalType: Sync,
58{
59}
60
61/// A guard which holds alive a snatchable wgpu-core resource and dereferences to the Hal type.
62struct SnatchableResourceGuard<Resource, HalType>
63where
64    Resource: RawResourceAccess,
65{
66    resource: Arc<Resource>,
67    snatch_lock_rank_data: ManuallyDrop<RankData>,
68    ptr: *const HalType,
69}
70
71impl<Resource, HalType> SnatchableResourceGuard<Resource, HalType>
72where
73    Resource: RawResourceAccess,
74    HalType: 'static,
75{
76    /// Creates a new guard from a snatchable resource.
77    ///
78    /// Returns `None` if:
79    /// - The resource is not of the expected Hal type.
80    /// - The resource has been destroyed.
81    pub fn new(resource: Arc<Resource>) -> Option<Self> {
82        // Grab the snatchable lock.
83        let snatch_guard = resource.device().snatchable_lock.read();
84
85        // Get the raw resource and downcast it to the expected Hal type.
86        let underlying = resource
87            .raw(&snatch_guard)?
88            .as_any()
89            .downcast_ref::<HalType>()?;
90
91        // Cast the raw resource to a pointer to get rid of the lifetime
92        // connecting us to the snatch guard.
93        let ptr: *const HalType = underlying;
94
95        // SAFETY: At this point all panicking or divergance has already happened,
96        // so we can safely forget the snatch guard without causing the lock to be left open.
97        let snatch_lock_rank_data = SnatchGuard::forget(snatch_guard);
98
99        // SAFETY: We only construct this guard while the snatchable lock is held,
100        // as the `drop` implementation of this guard will unsafely release the lock.
101        Some(Self {
102            resource,
103            snatch_lock_rank_data: ManuallyDrop::new(snatch_lock_rank_data),
104            ptr,
105        })
106    }
107}
108
109impl<Resource, HalType> Deref for SnatchableResourceGuard<Resource, HalType>
110where
111    Resource: RawResourceAccess,
112{
113    type Target = HalType;
114
115    fn deref(&self) -> &Self::Target {
116        // SAFETY: The pointer is guaranteed to be valid as the original resource is
117        // still alive and the snatchable lock is still being held due to the forgotten
118        // snatch guard.
119        unsafe { &*self.ptr }
120    }
121}
122
123impl<Resource, HalType> Drop for SnatchableResourceGuard<Resource, HalType>
124where
125    Resource: RawResourceAccess,
126{
127    fn drop(&mut self) {
128        // SAFETY:
129        // - We are not going to access the rank data anymore.
130        let data = unsafe { ManuallyDrop::take(&mut self.snatch_lock_rank_data) };
131
132        // SAFETY:
133        // - The pointer is no longer going to be accessed.
134        // - The snatchable lock is being held because this type was not created
135        //   until after the snatchable lock was forgotten.
136        unsafe {
137            self.resource
138                .device()
139                .snatchable_lock
140                .force_unlock_read(data)
141        };
142    }
143}
144
145unsafe impl<Resource, HalType> Send for SnatchableResourceGuard<Resource, HalType>
146where
147    Resource: RawResourceAccess + Send,
148    HalType: Send,
149{
150}
151unsafe impl<Resource, HalType> Sync for SnatchableResourceGuard<Resource, HalType>
152where
153    Resource: RawResourceAccess + Sync,
154    HalType: Sync,
155{
156}
157
158/// A guard which holds alive a device and the device's fence lock, dereferencing to the Hal type.
159struct FenceGuard<Fence> {
160    device: Arc<Device>,
161    fence_lock_rank_data: ManuallyDrop<RankData>,
162    ptr: *const Fence,
163}
164
165impl<Fence> FenceGuard<Fence>
166where
167    Fence: 'static,
168{
169    /// Creates a new guard over a device's fence.
170    ///
171    /// Returns `None` if:
172    /// - The device's fence is not of the expected Hal type.
173    pub fn new(device: Arc<Device>) -> Option<Self> {
174        // Grab the fence lock.
175        let fence_guard = device.fence.read();
176
177        // Get the raw fence and downcast it to the expected Hal type, coercing it to a pointer
178        // to get rid of the lifetime connecting us to the fence guard.
179        let ptr: *const Fence = fence_guard.as_any().downcast_ref::<Fence>()?;
180
181        // SAFETY: At this point all panicking or divergance has already happened,
182        // so we can safely forget the fence guard without causing the lock to be left open.
183        let fence_lock_rank_data = RwLockReadGuard::forget(fence_guard);
184
185        // SAFETY: We only construct this guard while the fence lock is held,
186        // as the `drop` implementation of this guard will unsafely release the lock.
187        Some(Self {
188            device,
189            fence_lock_rank_data: ManuallyDrop::new(fence_lock_rank_data),
190            ptr,
191        })
192    }
193}
194
195impl<Fence> Deref for FenceGuard<Fence> {
196    type Target = Fence;
197
198    fn deref(&self) -> &Self::Target {
199        // SAFETY: The pointer is guaranteed to be valid as the original device's fence
200        // is still alive and the fence lock is still being held due to the forgotten
201        // fence guard.
202        unsafe { &*self.ptr }
203    }
204}
205
206impl<Fence> Drop for FenceGuard<Fence> {
207    fn drop(&mut self) {
208        // SAFETY:
209        // - We are not going to access the rank data anymore.
210        let data = unsafe { ManuallyDrop::take(&mut self.fence_lock_rank_data) };
211
212        // SAFETY:
213        // - The pointer is no longer going to be accessed.
214        // - The fence lock is being held because this type was not created
215        //   until after the fence lock was forgotten.
216        unsafe {
217            self.device.fence.force_unlock_read(data);
218        };
219    }
220}
221
222unsafe impl<Fence> Send for FenceGuard<Fence> where Fence: Send {}
223unsafe impl<Fence> Sync for FenceGuard<Fence> where Fence: Sync {}
224
225impl Global {
226    /// # Safety
227    ///
228    /// - The raw buffer handle must not be manually destroyed
229    pub unsafe fn buffer_as_hal<A: HalApi>(
230        &self,
231        id: BufferId,
232    ) -> Option<impl Deref<Target = A::Buffer>> {
233        profiling::scope!("Buffer::as_hal");
234
235        let hub = &self.hub;
236
237        let buffer = hub.buffers.get(id).get().ok()?;
238
239        SnatchableResourceGuard::new(buffer)
240    }
241
242    /// # Safety
243    ///
244    /// - The raw texture handle must not be manually destroyed
245    pub unsafe fn texture_as_hal<A: HalApi>(
246        &self,
247        id: TextureId,
248    ) -> Option<impl Deref<Target = A::Texture>> {
249        profiling::scope!("Texture::as_hal");
250
251        let hub = &self.hub;
252
253        let texture = hub.textures.get(id).get().ok()?;
254
255        SnatchableResourceGuard::new(texture)
256    }
257
258    /// # Safety
259    ///
260    /// - The raw texture view handle must not be manually destroyed
261    pub unsafe fn texture_view_as_hal<A: HalApi>(
262        &self,
263        id: TextureViewId,
264    ) -> Option<impl Deref<Target = A::TextureView>> {
265        profiling::scope!("TextureView::as_hal");
266
267        let hub = &self.hub;
268
269        let view = hub.texture_views.get(id).get().ok()?;
270
271        SnatchableResourceGuard::new(view)
272    }
273
274    /// # Safety
275    ///
276    /// - The raw adapter handle must not be manually destroyed
277    pub unsafe fn adapter_as_hal<A: HalApi>(
278        &self,
279        id: AdapterId,
280    ) -> Option<impl Deref<Target = A::Adapter>> {
281        profiling::scope!("Adapter::as_hal");
282
283        let hub = &self.hub;
284        let adapter = hub.adapters.get(id);
285
286        SimpleResourceGuard::new(adapter, move |adapter| {
287            adapter.raw.adapter.as_any().downcast_ref()
288        })
289    }
290
291    /// # Safety
292    ///
293    /// - The raw device handle must not be manually destroyed
294    pub unsafe fn device_as_hal<A: HalApi>(
295        &self,
296        id: DeviceId,
297    ) -> Option<impl Deref<Target = A::Device>> {
298        profiling::scope!("Device::as_hal");
299
300        let device = self.hub.devices.get(id);
301
302        SimpleResourceGuard::new(device, move |device| device.raw().as_any().downcast_ref())
303    }
304
305    /// # Safety
306    ///
307    /// - The raw fence handle must not be manually destroyed
308    pub unsafe fn device_fence_as_hal<A: HalApi>(
309        &self,
310        id: DeviceId,
311    ) -> Option<impl Deref<Target = A::Fence>> {
312        profiling::scope!("Device::fence_as_hal");
313
314        let device = self.hub.devices.get(id);
315
316        FenceGuard::new(device)
317    }
318
319    /// # Safety
320    /// - The raw surface handle must not be manually destroyed
321    pub unsafe fn surface_as_hal<A: HalApi>(
322        &self,
323        id: SurfaceId,
324    ) -> Option<impl Deref<Target = A::Surface>> {
325        profiling::scope!("Surface::as_hal");
326
327        let surface = self.surfaces.get(id);
328
329        SimpleResourceGuard::new(surface, move |surface| {
330            surface.raw(A::VARIANT)?.as_any().downcast_ref()
331        })
332    }
333
334    /// # Safety
335    ///
336    /// - The raw command encoder handle must not be manually destroyed
337    pub unsafe fn command_encoder_as_hal_mut<
338        A: HalApi,
339        F: FnOnce(Option<&mut A::CommandEncoder>) -> R,
340        R,
341    >(
342        &self,
343        id: CommandEncoderId,
344        hal_command_encoder_callback: F,
345    ) -> R {
346        profiling::scope!("CommandEncoder::as_hal");
347
348        let hub = &self.hub;
349
350        let cmd_buf = hub.command_buffers.get(id.into_command_buffer_id());
351        let mut cmd_buf_data = cmd_buf.data.lock();
352        cmd_buf_data.record_as_hal_mut(|opt_cmd_buf| -> R {
353            hal_command_encoder_callback(opt_cmd_buf.and_then(|cmd_buf| {
354                cmd_buf
355                    .encoder
356                    .open()
357                    .ok()
358                    .and_then(|encoder| encoder.as_any_mut().downcast_mut())
359            }))
360        })
361    }
362
363    /// # Safety
364    ///
365    /// - The raw queue handle must not be manually destroyed
366    pub unsafe fn queue_as_hal<A: HalApi>(
367        &self,
368        id: QueueId,
369    ) -> Option<impl Deref<Target = A::Queue>> {
370        profiling::scope!("Queue::as_hal");
371
372        let queue = self.hub.queues.get(id);
373
374        SimpleResourceGuard::new(queue, move |queue| queue.raw().as_any().downcast_ref())
375    }
376
377    /// # Safety
378    ///
379    /// - The raw blas handle must not be manually destroyed
380    pub unsafe fn blas_as_hal<A: HalApi>(
381        &self,
382        id: BlasId,
383    ) -> Option<impl Deref<Target = A::AccelerationStructure>> {
384        profiling::scope!("Blas::as_hal");
385
386        let hub = &self.hub;
387
388        let blas = hub.blas_s.get(id).get().ok()?;
389
390        SnatchableResourceGuard::new(blas)
391    }
392
393    /// # Safety
394    ///
395    /// - The raw tlas handle must not be manually destroyed
396    pub unsafe fn tlas_as_hal<A: HalApi>(
397        &self,
398        id: TlasId,
399    ) -> Option<impl Deref<Target = A::AccelerationStructure>> {
400        profiling::scope!("Tlas::as_hal");
401
402        let hub = &self.hub;
403
404        let tlas = hub.tlas_s.get(id).get().ok()?;
405
406        SnatchableResourceGuard::new(tlas)
407    }
408}