wgpu_core/
as_hal.rs

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