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