wgpu_core/device/
global.rs

1#[cfg(feature = "trace")]
2use crate::device::trace;
3use crate::{
4    api_log, binding_model, command, conv,
5    device::{
6        bgl, life::WaitIdleError, map_buffer, queue, DeviceError, DeviceLostClosure,
7        DeviceLostReason, HostMap, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL,
8    },
9    global::Global,
10    hal_api::HalApi,
11    id::{self, AdapterId, DeviceId, QueueId, SurfaceId},
12    init_tracker::TextureInitTracker,
13    instance::{self, Adapter, Surface},
14    lock::{rank, RwLock},
15    pipeline, present,
16    resource::{self, BufferAccessResult},
17    resource::{BufferAccessError, BufferMapOperation, CreateBufferError, Resource},
18    validation::check_buffer_usage,
19    Label, LabelHelpers as _,
20};
21
22use arrayvec::ArrayVec;
23use hal::Device as _;
24
25use wgt::{BufferAddress, TextureFormat};
26
27use std::{
28    borrow::Cow,
29    iter, ptr,
30    sync::{atomic::Ordering, Arc},
31};
32
33use super::{ImplicitPipelineIds, InvalidDevice, UserClosures};
34
35impl Global {
36    pub fn adapter_is_surface_supported<A: HalApi>(
37        &self,
38        adapter_id: AdapterId,
39        surface_id: SurfaceId,
40    ) -> Result<bool, instance::IsSurfaceSupportedError> {
41        let hub = A::hub(self);
42
43        let surface_guard = self.surfaces.read();
44        let adapter_guard = hub.adapters.read();
45        let adapter = adapter_guard
46            .get(adapter_id)
47            .map_err(|_| instance::IsSurfaceSupportedError::InvalidAdapter)?;
48        let surface = surface_guard
49            .get(surface_id)
50            .map_err(|_| instance::IsSurfaceSupportedError::InvalidSurface)?;
51        Ok(adapter.is_surface_supported(surface))
52    }
53
54    pub fn surface_get_capabilities<A: HalApi>(
55        &self,
56        surface_id: SurfaceId,
57        adapter_id: AdapterId,
58    ) -> Result<wgt::SurfaceCapabilities, instance::GetSurfaceSupportError> {
59        profiling::scope!("Surface::get_capabilities");
60        self.fetch_adapter_and_surface::<A, _, _>(surface_id, adapter_id, |adapter, surface| {
61            let mut hal_caps = surface.get_capabilities(adapter)?;
62
63            hal_caps.formats.sort_by_key(|f| !f.is_srgb());
64
65            let usages = conv::map_texture_usage_from_hal(hal_caps.usage);
66
67            Ok(wgt::SurfaceCapabilities {
68                formats: hal_caps.formats,
69                present_modes: hal_caps.present_modes,
70                alpha_modes: hal_caps.composite_alpha_modes,
71                usages,
72            })
73        })
74    }
75
76    fn fetch_adapter_and_surface<
77        A: HalApi,
78        F: FnOnce(&Adapter<A>, &Surface) -> Result<B, instance::GetSurfaceSupportError>,
79        B,
80    >(
81        &self,
82        surface_id: SurfaceId,
83        adapter_id: AdapterId,
84        get_supported_callback: F,
85    ) -> Result<B, instance::GetSurfaceSupportError> {
86        let hub = A::hub(self);
87
88        let surface_guard = self.surfaces.read();
89        let adapter_guard = hub.adapters.read();
90        let adapter = adapter_guard
91            .get(adapter_id)
92            .map_err(|_| instance::GetSurfaceSupportError::InvalidAdapter)?;
93        let surface = surface_guard
94            .get(surface_id)
95            .map_err(|_| instance::GetSurfaceSupportError::InvalidSurface)?;
96
97        get_supported_callback(adapter, surface)
98    }
99
100    pub fn device_features<A: HalApi>(
101        &self,
102        device_id: DeviceId,
103    ) -> Result<wgt::Features, InvalidDevice> {
104        let hub = A::hub(self);
105
106        let device = hub.devices.get(device_id).map_err(|_| InvalidDevice)?;
107        if !device.is_valid() {
108            return Err(InvalidDevice);
109        }
110
111        Ok(device.features)
112    }
113
114    pub fn device_limits<A: HalApi>(
115        &self,
116        device_id: DeviceId,
117    ) -> Result<wgt::Limits, InvalidDevice> {
118        let hub = A::hub(self);
119
120        let device = hub.devices.get(device_id).map_err(|_| InvalidDevice)?;
121        if !device.is_valid() {
122            return Err(InvalidDevice);
123        }
124
125        Ok(device.limits.clone())
126    }
127
128    pub fn device_downlevel_properties<A: HalApi>(
129        &self,
130        device_id: DeviceId,
131    ) -> Result<wgt::DownlevelCapabilities, InvalidDevice> {
132        let hub = A::hub(self);
133
134        let device = hub.devices.get(device_id).map_err(|_| InvalidDevice)?;
135        if !device.is_valid() {
136            return Err(InvalidDevice);
137        }
138
139        Ok(device.downlevel.clone())
140    }
141
142    pub fn device_create_buffer<A: HalApi>(
143        &self,
144        device_id: DeviceId,
145        desc: &resource::BufferDescriptor,
146        id_in: Option<id::BufferId>,
147    ) -> (id::BufferId, Option<CreateBufferError>) {
148        profiling::scope!("Device::create_buffer");
149
150        let hub = A::hub(self);
151        let fid = hub.buffers.prepare(id_in);
152
153        let mut to_destroy: ArrayVec<resource::Buffer<A>, 2> = ArrayVec::new();
154        let error = loop {
155            let device = match hub.devices.get(device_id) {
156                Ok(device) => device,
157                Err(_) => {
158                    break DeviceError::Invalid.into();
159                }
160            };
161            if !device.is_valid() {
162                break DeviceError::Lost.into();
163            }
164
165            if desc.usage.is_empty() {
166                // Per spec, `usage` must not be zero.
167                break CreateBufferError::InvalidUsage(desc.usage);
168            }
169
170            #[cfg(feature = "trace")]
171            if let Some(ref mut trace) = *device.trace.lock() {
172                let mut desc = desc.clone();
173                let mapped_at_creation = std::mem::replace(&mut desc.mapped_at_creation, false);
174                if mapped_at_creation && !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
175                    desc.usage |= wgt::BufferUsages::COPY_DST;
176                }
177                trace.add(trace::Action::CreateBuffer(fid.id(), desc));
178            }
179
180            let buffer = match device.create_buffer(desc, false) {
181                Ok(buffer) => buffer,
182                Err(e) => {
183                    break e;
184                }
185            };
186
187            let buffer_use = if !desc.mapped_at_creation {
188                hal::BufferUses::empty()
189            } else if desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
190                // buffer is mappable, so we are just doing that at start
191                let map_size = buffer.size;
192                let ptr = if map_size == 0 {
193                    std::ptr::NonNull::dangling()
194                } else {
195                    let snatch_guard = device.snatchable_lock.read();
196                    match map_buffer(
197                        device.raw(),
198                        &buffer,
199                        0,
200                        map_size,
201                        HostMap::Write,
202                        &snatch_guard,
203                    ) {
204                        Ok(ptr) => ptr,
205                        Err(e) => {
206                            to_destroy.push(buffer);
207                            break e.into();
208                        }
209                    }
210                };
211                *buffer.map_state.lock() = resource::BufferMapState::Active {
212                    ptr,
213                    range: 0..map_size,
214                    host: HostMap::Write,
215                };
216                hal::BufferUses::MAP_WRITE
217            } else {
218                // buffer needs staging area for initialization only
219                let stage_desc = wgt::BufferDescriptor {
220                    label: Some(Cow::Borrowed(
221                        "(wgpu internal) initializing unmappable buffer",
222                    )),
223                    size: desc.size,
224                    usage: wgt::BufferUsages::MAP_WRITE | wgt::BufferUsages::COPY_SRC,
225                    mapped_at_creation: false,
226                };
227                let stage = match device.create_buffer(&stage_desc, true) {
228                    Ok(stage) => Arc::new(stage),
229                    Err(e) => {
230                        to_destroy.push(buffer);
231                        break e;
232                    }
233                };
234
235                let snatch_guard = device.snatchable_lock.read();
236                let stage_raw = stage.raw(&snatch_guard).unwrap();
237                let mapping = match unsafe { device.raw().map_buffer(stage_raw, 0..stage.size) } {
238                    Ok(mapping) => mapping,
239                    Err(e) => {
240                        to_destroy.push(buffer);
241                        break CreateBufferError::Device(e.into());
242                    }
243                };
244
245                assert_eq!(buffer.size % wgt::COPY_BUFFER_ALIGNMENT, 0);
246                // Zero initialize memory and then mark both staging and buffer as initialized
247                // (it's guaranteed that this is the case by the time the buffer is usable)
248                unsafe { ptr::write_bytes(mapping.ptr.as_ptr(), 0, buffer.size as usize) };
249                buffer.initialization_status.write().drain(0..buffer.size);
250                stage.initialization_status.write().drain(0..buffer.size);
251
252                *buffer.map_state.lock() = resource::BufferMapState::Init {
253                    ptr: mapping.ptr,
254                    needs_flush: !mapping.is_coherent,
255                    stage_buffer: stage,
256                };
257                hal::BufferUses::COPY_DST
258            };
259
260            let (id, resource) = fid.assign(Arc::new(buffer));
261            api_log!("Device::create_buffer({desc:?}) -> {id:?}");
262
263            device
264                .trackers
265                .lock()
266                .buffers
267                .insert_single(resource, buffer_use);
268
269            return (id, None);
270        };
271
272        // Error path
273
274        for buffer in to_destroy {
275            let device = Arc::clone(&buffer.device);
276            device
277                .lock_life()
278                .schedule_resource_destruction(queue::TempResource::Buffer(Arc::new(buffer)), !0);
279        }
280
281        let id = fid.assign_error(desc.label.borrow_or_default());
282        (id, Some(error))
283    }
284
285    /// Assign `id_in` an error with the given `label`.
286    ///
287    /// Ensure that future attempts to use `id_in` as a buffer ID will propagate
288    /// the error, following the WebGPU ["contagious invalidity"] style.
289    ///
290    /// Firefox uses this function to comply strictly with the WebGPU spec,
291    /// which requires [`GPUBufferDescriptor`] validation to be generated on the
292    /// Device timeline and leave the newly created [`GPUBuffer`] invalid.
293    ///
294    /// Ideally, we would simply let [`device_create_buffer`] take care of all
295    /// of this, but some errors must be detected before we can even construct a
296    /// [`wgpu_types::BufferDescriptor`] to give it. For example, the WebGPU API
297    /// allows a `GPUBufferDescriptor`'s [`usage`] property to be any WebIDL
298    /// `unsigned long` value, but we can't construct a
299    /// [`wgpu_types::BufferUsages`] value from values with unassigned bits
300    /// set. This means we must validate `usage` before we can call
301    /// `device_create_buffer`.
302    ///
303    /// When that validation fails, we must arrange for the buffer id to be
304    /// considered invalid. This method provides the means to do so.
305    ///
306    /// ["contagious invalidity"]: https://www.w3.org/TR/webgpu/#invalidity
307    /// [`GPUBufferDescriptor`]: https://www.w3.org/TR/webgpu/#dictdef-gpubufferdescriptor
308    /// [`GPUBuffer`]: https://www.w3.org/TR/webgpu/#gpubuffer
309    /// [`wgpu_types::BufferDescriptor`]: wgt::BufferDescriptor
310    /// [`device_create_buffer`]: Global::device_create_buffer
311    /// [`usage`]: https://www.w3.org/TR/webgpu/#dom-gputexturedescriptor-usage
312    /// [`wgpu_types::BufferUsages`]: wgt::BufferUsages
313    pub fn create_buffer_error<A: HalApi>(&self, id_in: Option<id::BufferId>, label: Label) {
314        let hub = A::hub(self);
315        let fid = hub.buffers.prepare(id_in);
316
317        fid.assign_error(label.borrow_or_default());
318    }
319
320    pub fn create_render_bundle_error<A: HalApi>(
321        &self,
322        id_in: Option<id::RenderBundleId>,
323        label: Label,
324    ) {
325        let hub = A::hub(self);
326        let fid = hub.render_bundles.prepare(id_in);
327
328        fid.assign_error(label.borrow_or_default());
329    }
330
331    /// Assign `id_in` an error with the given `label`.
332    ///
333    /// See `create_buffer_error` for more context and explanation.
334    pub fn create_texture_error<A: HalApi>(&self, id_in: Option<id::TextureId>, label: Label) {
335        let hub = A::hub(self);
336        let fid = hub.textures.prepare(id_in);
337
338        fid.assign_error(label.borrow_or_default());
339    }
340
341    #[cfg(feature = "replay")]
342    pub fn device_wait_for_buffer<A: HalApi>(
343        &self,
344        device_id: DeviceId,
345        buffer_id: id::BufferId,
346    ) -> Result<(), WaitIdleError> {
347        let hub = A::hub(self);
348
349        let last_submission = {
350            let buffer_guard = hub.buffers.write();
351            match buffer_guard.get(buffer_id) {
352                Ok(buffer) => buffer.info.submission_index(),
353                Err(_) => return Ok(()),
354            }
355        };
356
357        hub.devices
358            .get(device_id)
359            .map_err(|_| DeviceError::Invalid)?
360            .wait_for_submit(last_submission)
361    }
362
363    #[doc(hidden)]
364    pub fn device_set_buffer_sub_data<A: HalApi>(
365        &self,
366        device_id: DeviceId,
367        buffer_id: id::BufferId,
368        offset: BufferAddress,
369        data: &[u8],
370    ) -> BufferAccessResult {
371        profiling::scope!("Device::set_buffer_sub_data");
372
373        let hub = A::hub(self);
374
375        let device = hub
376            .devices
377            .get(device_id)
378            .map_err(|_| DeviceError::Invalid)?;
379        let snatch_guard = device.snatchable_lock.read();
380        if !device.is_valid() {
381            return Err(DeviceError::Lost.into());
382        }
383
384        let buffer = hub
385            .buffers
386            .get(buffer_id)
387            .map_err(|_| BufferAccessError::Invalid)?;
388        check_buffer_usage(buffer_id, buffer.usage, wgt::BufferUsages::MAP_WRITE)?;
389        //assert!(buffer isn't used by the GPU);
390
391        #[cfg(feature = "trace")]
392        if let Some(ref mut trace) = *device.trace.lock() {
393            let data_path = trace.make_binary("bin", data);
394            trace.add(trace::Action::WriteBuffer {
395                id: buffer_id,
396                data: data_path,
397                range: offset..offset + data.len() as BufferAddress,
398                queued: false,
399            });
400        }
401
402        let raw_buf = buffer
403            .raw(&snatch_guard)
404            .ok_or(BufferAccessError::Destroyed)?;
405        unsafe {
406            let mapping = device
407                .raw()
408                .map_buffer(raw_buf, offset..offset + data.len() as u64)
409                .map_err(DeviceError::from)?;
410            ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len());
411            if !mapping.is_coherent {
412                device
413                    .raw()
414                    .flush_mapped_ranges(raw_buf, iter::once(offset..offset + data.len() as u64));
415            }
416            device
417                .raw()
418                .unmap_buffer(raw_buf)
419                .map_err(DeviceError::from)?;
420        }
421
422        Ok(())
423    }
424
425    #[doc(hidden)]
426    pub fn device_get_buffer_sub_data<A: HalApi>(
427        &self,
428        device_id: DeviceId,
429        buffer_id: id::BufferId,
430        offset: BufferAddress,
431        data: &mut [u8],
432    ) -> BufferAccessResult {
433        profiling::scope!("Device::get_buffer_sub_data");
434
435        let hub = A::hub(self);
436
437        let device = hub
438            .devices
439            .get(device_id)
440            .map_err(|_| DeviceError::Invalid)?;
441        if !device.is_valid() {
442            return Err(DeviceError::Lost.into());
443        }
444
445        let snatch_guard = device.snatchable_lock.read();
446
447        let buffer = hub
448            .buffers
449            .get(buffer_id)
450            .map_err(|_| BufferAccessError::Invalid)?;
451        check_buffer_usage(buffer_id, buffer.usage, wgt::BufferUsages::MAP_READ)?;
452        //assert!(buffer isn't used by the GPU);
453
454        let raw_buf = buffer
455            .raw(&snatch_guard)
456            .ok_or(BufferAccessError::Destroyed)?;
457        unsafe {
458            let mapping = device
459                .raw()
460                .map_buffer(raw_buf, offset..offset + data.len() as u64)
461                .map_err(DeviceError::from)?;
462            if !mapping.is_coherent {
463                device.raw().invalidate_mapped_ranges(
464                    raw_buf,
465                    iter::once(offset..offset + data.len() as u64),
466                );
467            }
468            ptr::copy_nonoverlapping(mapping.ptr.as_ptr(), data.as_mut_ptr(), data.len());
469            device
470                .raw()
471                .unmap_buffer(raw_buf)
472                .map_err(DeviceError::from)?;
473        }
474
475        Ok(())
476    }
477
478    pub fn buffer_label<A: HalApi>(&self, id: id::BufferId) -> String {
479        A::hub(self).buffers.label_for_resource(id)
480    }
481
482    pub fn buffer_destroy<A: HalApi>(
483        &self,
484        buffer_id: id::BufferId,
485    ) -> Result<(), resource::DestroyError> {
486        profiling::scope!("Buffer::destroy");
487        api_log!("Buffer::destroy {buffer_id:?}");
488
489        let hub = A::hub(self);
490
491        let buffer = hub
492            .buffers
493            .get(buffer_id)
494            .map_err(|_| resource::DestroyError::Invalid)?;
495
496        let _ = buffer.unmap();
497
498        buffer.destroy()
499    }
500
501    pub fn buffer_drop<A: HalApi>(&self, buffer_id: id::BufferId, wait: bool) {
502        profiling::scope!("Buffer::drop");
503        api_log!("Buffer::drop {buffer_id:?}");
504
505        let hub = A::hub(self);
506
507        let buffer = match hub.buffers.unregister(buffer_id) {
508            Some(buffer) => buffer,
509            None => {
510                return;
511            }
512        };
513
514        let _ = buffer.unmap();
515
516        let last_submit_index = buffer.info.submission_index();
517
518        let device = buffer.device.clone();
519
520        if device
521            .pending_writes
522            .lock()
523            .as_ref()
524            .unwrap()
525            .dst_buffers
526            .contains_key(&buffer_id)
527        {
528            device.lock_life().future_suspected_buffers.push(buffer);
529        } else {
530            device
531                .lock_life()
532                .suspected_resources
533                .buffers
534                .insert(buffer.info.tracker_index(), buffer);
535        }
536
537        if wait {
538            match device.wait_for_submit(last_submit_index) {
539                Ok(()) => (),
540                Err(e) => log::error!("Failed to wait for buffer {:?}: {}", buffer_id, e),
541            }
542        }
543    }
544
545    pub fn device_create_texture<A: HalApi>(
546        &self,
547        device_id: DeviceId,
548        desc: &resource::TextureDescriptor,
549        id_in: Option<id::TextureId>,
550    ) -> (id::TextureId, Option<resource::CreateTextureError>) {
551        profiling::scope!("Device::create_texture");
552
553        let hub = A::hub(self);
554
555        let fid = hub.textures.prepare(id_in);
556
557        let error = loop {
558            let device = match hub.devices.get(device_id) {
559                Ok(device) => device,
560                Err(_) => break DeviceError::Invalid.into(),
561            };
562            if !device.is_valid() {
563                break DeviceError::Lost.into();
564            }
565            #[cfg(feature = "trace")]
566            if let Some(ref mut trace) = *device.trace.lock() {
567                trace.add(trace::Action::CreateTexture(fid.id(), desc.clone()));
568            }
569
570            let texture = match device.create_texture(&device.adapter, desc) {
571                Ok(texture) => texture,
572                Err(error) => break error,
573            };
574
575            let (id, resource) = fid.assign(Arc::new(texture));
576            api_log!("Device::create_texture({desc:?}) -> {id:?}");
577
578            device
579                .trackers
580                .lock()
581                .textures
582                .insert_single(resource, hal::TextureUses::UNINITIALIZED);
583
584            return (id, None);
585        };
586
587        log::error!("Device::create_texture error: {error}");
588
589        let id = fid.assign_error(desc.label.borrow_or_default());
590        (id, Some(error))
591    }
592
593    /// # Safety
594    ///
595    /// - `hal_texture` must be created from `device_id` corresponding raw handle.
596    /// - `hal_texture` must be created respecting `desc`
597    /// - `hal_texture` must be initialized
598    pub unsafe fn create_texture_from_hal<A: HalApi>(
599        &self,
600        hal_texture: A::Texture,
601        device_id: DeviceId,
602        desc: &resource::TextureDescriptor,
603        id_in: Option<id::TextureId>,
604    ) -> (id::TextureId, Option<resource::CreateTextureError>) {
605        profiling::scope!("Device::create_texture_from_hal");
606
607        let hub = A::hub(self);
608
609        let fid = hub.textures.prepare(id_in);
610
611        let error = loop {
612            let device = match hub.devices.get(device_id) {
613                Ok(device) => device,
614                Err(_) => break DeviceError::Invalid.into(),
615            };
616            if !device.is_valid() {
617                break DeviceError::Lost.into();
618            }
619
620            // NB: Any change done through the raw texture handle will not be
621            // recorded in the replay
622            #[cfg(feature = "trace")]
623            if let Some(ref mut trace) = *device.trace.lock() {
624                trace.add(trace::Action::CreateTexture(fid.id(), desc.clone()));
625            }
626
627            let format_features = match device
628                .describe_format_features(&device.adapter, desc.format)
629                .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))
630            {
631                Ok(features) => features,
632                Err(error) => break error,
633            };
634
635            let mut texture = device.create_texture_from_hal(
636                hal_texture,
637                conv::map_texture_usage(desc.usage, desc.format.into()),
638                desc,
639                format_features,
640                resource::TextureClearMode::None,
641            );
642            if desc.usage.contains(wgt::TextureUsages::COPY_DST) {
643                texture.hal_usage |= hal::TextureUses::COPY_DST;
644            }
645
646            texture.initialization_status = RwLock::new(
647                rank::TEXTURE_INITIALIZATION_STATUS,
648                TextureInitTracker::new(desc.mip_level_count, 0),
649            );
650
651            let (id, resource) = fid.assign(Arc::new(texture));
652            api_log!("Device::create_texture({desc:?}) -> {id:?}");
653
654            device
655                .trackers
656                .lock()
657                .textures
658                .insert_single(resource, hal::TextureUses::UNINITIALIZED);
659
660            return (id, None);
661        };
662
663        log::error!("Device::create_texture error: {error}");
664
665        let id = fid.assign_error(desc.label.borrow_or_default());
666        (id, Some(error))
667    }
668
669    /// # Safety
670    ///
671    /// - `hal_buffer` must be created from `device_id` corresponding raw handle.
672    /// - `hal_buffer` must be created respecting `desc`
673    /// - `hal_buffer` must be initialized
674    pub unsafe fn create_buffer_from_hal<A: HalApi>(
675        &self,
676        hal_buffer: A::Buffer,
677        device_id: DeviceId,
678        desc: &resource::BufferDescriptor,
679        id_in: Option<id::BufferId>,
680    ) -> (id::BufferId, Option<CreateBufferError>) {
681        profiling::scope!("Device::create_buffer");
682
683        let hub = A::hub(self);
684        let fid = hub.buffers.prepare(id_in);
685
686        let error = loop {
687            let device = match hub.devices.get(device_id) {
688                Ok(device) => device,
689                Err(_) => break DeviceError::Invalid.into(),
690            };
691            if !device.is_valid() {
692                break DeviceError::Lost.into();
693            }
694
695            // NB: Any change done through the raw buffer handle will not be
696            // recorded in the replay
697            #[cfg(feature = "trace")]
698            if let Some(trace) = device.trace.lock().as_mut() {
699                trace.add(trace::Action::CreateBuffer(fid.id(), desc.clone()));
700            }
701
702            let buffer = device.create_buffer_from_hal(hal_buffer, desc);
703
704            let (id, buffer) = fid.assign(Arc::new(buffer));
705            api_log!("Device::create_buffer -> {id:?}");
706
707            device
708                .trackers
709                .lock()
710                .buffers
711                .insert_single(buffer, hal::BufferUses::empty());
712
713            return (id, None);
714        };
715
716        log::error!("Device::create_buffer error: {error}");
717
718        let id = fid.assign_error(desc.label.borrow_or_default());
719        (id, Some(error))
720    }
721
722    pub fn texture_label<A: HalApi>(&self, id: id::TextureId) -> String {
723        A::hub(self).textures.label_for_resource(id)
724    }
725
726    pub fn texture_destroy<A: HalApi>(
727        &self,
728        texture_id: id::TextureId,
729    ) -> Result<(), resource::DestroyError> {
730        profiling::scope!("Texture::destroy");
731        api_log!("Texture::destroy {texture_id:?}");
732
733        let hub = A::hub(self);
734
735        let texture = hub
736            .textures
737            .get(texture_id)
738            .map_err(|_| resource::DestroyError::Invalid)?;
739
740        texture.destroy()
741    }
742
743    pub fn texture_drop<A: HalApi>(&self, texture_id: id::TextureId, wait: bool) {
744        profiling::scope!("Texture::drop");
745        api_log!("Texture::drop {texture_id:?}");
746
747        let hub = A::hub(self);
748
749        if let Some(texture) = hub.textures.unregister(texture_id) {
750            let last_submit_index = texture.info.submission_index();
751
752            let device = &texture.device;
753            {
754                if device
755                    .pending_writes
756                    .lock()
757                    .as_ref()
758                    .unwrap()
759                    .dst_textures
760                    .contains_key(&texture_id)
761                {
762                    device
763                        .lock_life()
764                        .future_suspected_textures
765                        .push(texture.clone());
766                } else {
767                    device
768                        .lock_life()
769                        .suspected_resources
770                        .textures
771                        .insert(texture.info.tracker_index(), texture.clone());
772                }
773            }
774
775            if wait {
776                match device.wait_for_submit(last_submit_index) {
777                    Ok(()) => (),
778                    Err(e) => log::error!("Failed to wait for texture {texture_id:?}: {e}"),
779                }
780            }
781        }
782    }
783
784    #[allow(unused_unsafe)]
785    pub fn texture_create_view<A: HalApi>(
786        &self,
787        texture_id: id::TextureId,
788        desc: &resource::TextureViewDescriptor,
789        id_in: Option<id::TextureViewId>,
790    ) -> (id::TextureViewId, Option<resource::CreateTextureViewError>) {
791        profiling::scope!("Texture::create_view");
792
793        let hub = A::hub(self);
794
795        let fid = hub.texture_views.prepare(id_in);
796
797        let error = loop {
798            let texture = match hub.textures.get(texture_id) {
799                Ok(texture) => texture,
800                Err(_) => break resource::CreateTextureViewError::InvalidTexture,
801            };
802            let device = &texture.device;
803            {
804                let snatch_guard = device.snatchable_lock.read();
805                if texture.is_destroyed(&snatch_guard) {
806                    break resource::CreateTextureViewError::InvalidTexture;
807                }
808            }
809            #[cfg(feature = "trace")]
810            if let Some(ref mut trace) = *device.trace.lock() {
811                trace.add(trace::Action::CreateTextureView {
812                    id: fid.id(),
813                    parent_id: texture_id,
814                    desc: desc.clone(),
815                });
816            }
817
818            let view = match unsafe { device.create_texture_view(&texture, desc) } {
819                Ok(view) => view,
820                Err(e) => break e,
821            };
822
823            let (id, resource) = fid.assign(Arc::new(view));
824
825            {
826                let mut views = texture.views.lock();
827                views.push(Arc::downgrade(&resource));
828            }
829
830            api_log!("Texture::create_view({texture_id:?}) -> {id:?}");
831            device.trackers.lock().views.insert_single(resource);
832            return (id, None);
833        };
834
835        log::error!("Texture::create_view({texture_id:?}) error: {error}");
836        let id = fid.assign_error(desc.label.borrow_or_default());
837        (id, Some(error))
838    }
839
840    pub fn texture_view_label<A: HalApi>(&self, id: id::TextureViewId) -> String {
841        A::hub(self).texture_views.label_for_resource(id)
842    }
843
844    pub fn texture_view_drop<A: HalApi>(
845        &self,
846        texture_view_id: id::TextureViewId,
847        wait: bool,
848    ) -> Result<(), resource::TextureViewDestroyError> {
849        profiling::scope!("TextureView::drop");
850        api_log!("TextureView::drop {texture_view_id:?}");
851
852        let hub = A::hub(self);
853
854        if let Some(view) = hub.texture_views.unregister(texture_view_id) {
855            let last_submit_index = view.info.submission_index();
856
857            view.device
858                .lock_life()
859                .suspected_resources
860                .texture_views
861                .insert(view.info.tracker_index(), view.clone());
862
863            if wait {
864                match view.device.wait_for_submit(last_submit_index) {
865                    Ok(()) => (),
866                    Err(e) => {
867                        log::error!("Failed to wait for texture view {texture_view_id:?}: {e}")
868                    }
869                }
870            }
871        }
872        Ok(())
873    }
874
875    pub fn device_create_sampler<A: HalApi>(
876        &self,
877        device_id: DeviceId,
878        desc: &resource::SamplerDescriptor,
879        id_in: Option<id::SamplerId>,
880    ) -> (id::SamplerId, Option<resource::CreateSamplerError>) {
881        profiling::scope!("Device::create_sampler");
882
883        let hub = A::hub(self);
884        let fid = hub.samplers.prepare(id_in);
885
886        let error = loop {
887            let device = match hub.devices.get(device_id) {
888                Ok(device) => device,
889                Err(_) => break DeviceError::Invalid.into(),
890            };
891            if !device.is_valid() {
892                break DeviceError::Lost.into();
893            }
894
895            #[cfg(feature = "trace")]
896            if let Some(ref mut trace) = *device.trace.lock() {
897                trace.add(trace::Action::CreateSampler(fid.id(), desc.clone()));
898            }
899
900            let sampler = match device.create_sampler(desc) {
901                Ok(sampler) => sampler,
902                Err(e) => break e,
903            };
904
905            let (id, resource) = fid.assign(Arc::new(sampler));
906            api_log!("Device::create_sampler -> {id:?}");
907            device.trackers.lock().samplers.insert_single(resource);
908
909            return (id, None);
910        };
911
912        let id = fid.assign_error(desc.label.borrow_or_default());
913        (id, Some(error))
914    }
915
916    pub fn sampler_label<A: HalApi>(&self, id: id::SamplerId) -> String {
917        A::hub(self).samplers.label_for_resource(id)
918    }
919
920    pub fn sampler_drop<A: HalApi>(&self, sampler_id: id::SamplerId) {
921        profiling::scope!("Sampler::drop");
922        api_log!("Sampler::drop {sampler_id:?}");
923
924        let hub = A::hub(self);
925
926        if let Some(sampler) = hub.samplers.unregister(sampler_id) {
927            sampler
928                .device
929                .lock_life()
930                .suspected_resources
931                .samplers
932                .insert(sampler.info.tracker_index(), sampler.clone());
933        }
934    }
935
936    pub fn device_create_bind_group_layout<A: HalApi>(
937        &self,
938        device_id: DeviceId,
939        desc: &binding_model::BindGroupLayoutDescriptor,
940        id_in: Option<id::BindGroupLayoutId>,
941    ) -> (
942        id::BindGroupLayoutId,
943        Option<binding_model::CreateBindGroupLayoutError>,
944    ) {
945        profiling::scope!("Device::create_bind_group_layout");
946
947        let hub = A::hub(self);
948        let fid = hub.bind_group_layouts.prepare(id_in);
949
950        let error = loop {
951            let device = match hub.devices.get(device_id) {
952                Ok(device) => device,
953                Err(_) => break DeviceError::Invalid.into(),
954            };
955            if !device.is_valid() {
956                break DeviceError::Lost.into();
957            }
958
959            #[cfg(feature = "trace")]
960            if let Some(ref mut trace) = *device.trace.lock() {
961                trace.add(trace::Action::CreateBindGroupLayout(fid.id(), desc.clone()));
962            }
963
964            let entry_map = match bgl::EntryMap::from_entries(&device.limits, &desc.entries) {
965                Ok(map) => map,
966                Err(e) => break e,
967            };
968
969            // Currently we make a distinction between fid.assign and fid.assign_existing. This distinction is incorrect,
970            // but see https://github.com/gfx-rs/wgpu/issues/4912.
971            //
972            // `assign` also registers the ID with the resource info, so it can be automatically reclaimed. This needs to
973            // happen with a mutable reference, which means it can only happen on creation.
974            //
975            // Because we need to call `assign` inside the closure (to get mut access), we need to "move" the future id into the closure.
976            // Rust cannot figure out at compile time that we only ever consume the ID once, so we need to move the check
977            // to runtime using an Option.
978            let mut fid = Some(fid);
979
980            // The closure might get called, and it might give us an ID. Side channel it out of the closure.
981            let mut id = None;
982
983            let bgl_result = device.bgl_pool.get_or_init(entry_map, |entry_map| {
984                let bgl =
985                    device.create_bind_group_layout(&desc.label, entry_map, bgl::Origin::Pool)?;
986
987                let (id_inner, arc) = fid.take().unwrap().assign(Arc::new(bgl));
988                id = Some(id_inner);
989
990                Ok(arc)
991            });
992
993            let layout = match bgl_result {
994                Ok(layout) => layout,
995                Err(e) => break e,
996            };
997
998            // If the ID was not assigned, and we survived the above check,
999            // it means that the bind group layout already existed and we need to call `assign_existing`.
1000            //
1001            // Calling this function _will_ leak the ID. See https://github.com/gfx-rs/wgpu/issues/4912.
1002            if id.is_none() {
1003                id = Some(fid.take().unwrap().assign_existing(&layout))
1004            }
1005
1006            api_log!("Device::create_bind_group_layout -> {id:?}");
1007            return (id.unwrap(), None);
1008        };
1009
1010        let fid = hub.bind_group_layouts.prepare(id_in);
1011        let id = fid.assign_error(desc.label.borrow_or_default());
1012        (id, Some(error))
1013    }
1014
1015    pub fn bind_group_layout_label<A: HalApi>(&self, id: id::BindGroupLayoutId) -> String {
1016        A::hub(self).bind_group_layouts.label_for_resource(id)
1017    }
1018
1019    pub fn bind_group_layout_drop<A: HalApi>(&self, bind_group_layout_id: id::BindGroupLayoutId) {
1020        profiling::scope!("BindGroupLayout::drop");
1021        api_log!("BindGroupLayout::drop {bind_group_layout_id:?}");
1022
1023        let hub = A::hub(self);
1024
1025        if let Some(layout) = hub.bind_group_layouts.unregister(bind_group_layout_id) {
1026            layout
1027                .device
1028                .lock_life()
1029                .suspected_resources
1030                .bind_group_layouts
1031                .insert(layout.info.tracker_index(), layout.clone());
1032        }
1033    }
1034
1035    pub fn device_create_pipeline_layout<A: HalApi>(
1036        &self,
1037        device_id: DeviceId,
1038        desc: &binding_model::PipelineLayoutDescriptor,
1039        id_in: Option<id::PipelineLayoutId>,
1040    ) -> (
1041        id::PipelineLayoutId,
1042        Option<binding_model::CreatePipelineLayoutError>,
1043    ) {
1044        profiling::scope!("Device::create_pipeline_layout");
1045
1046        let hub = A::hub(self);
1047        let fid = hub.pipeline_layouts.prepare(id_in);
1048
1049        let error = loop {
1050            let device = match hub.devices.get(device_id) {
1051                Ok(device) => device,
1052                Err(_) => break DeviceError::Invalid.into(),
1053            };
1054            if !device.is_valid() {
1055                break DeviceError::Lost.into();
1056            }
1057
1058            #[cfg(feature = "trace")]
1059            if let Some(ref mut trace) = *device.trace.lock() {
1060                trace.add(trace::Action::CreatePipelineLayout(fid.id(), desc.clone()));
1061            }
1062
1063            let layout = match device.create_pipeline_layout(desc, &hub.bind_group_layouts) {
1064                Ok(layout) => layout,
1065                Err(e) => break e,
1066            };
1067
1068            let (id, _) = fid.assign(Arc::new(layout));
1069            api_log!("Device::create_pipeline_layout -> {id:?}");
1070            return (id, None);
1071        };
1072
1073        let id = fid.assign_error(desc.label.borrow_or_default());
1074        (id, Some(error))
1075    }
1076
1077    pub fn pipeline_layout_label<A: HalApi>(&self, id: id::PipelineLayoutId) -> String {
1078        A::hub(self).pipeline_layouts.label_for_resource(id)
1079    }
1080
1081    pub fn pipeline_layout_drop<A: HalApi>(&self, pipeline_layout_id: id::PipelineLayoutId) {
1082        profiling::scope!("PipelineLayout::drop");
1083        api_log!("PipelineLayout::drop {pipeline_layout_id:?}");
1084
1085        let hub = A::hub(self);
1086        if let Some(layout) = hub.pipeline_layouts.unregister(pipeline_layout_id) {
1087            layout
1088                .device
1089                .lock_life()
1090                .suspected_resources
1091                .pipeline_layouts
1092                .insert(layout.info.tracker_index(), layout.clone());
1093        }
1094    }
1095
1096    pub fn device_create_bind_group<A: HalApi>(
1097        &self,
1098        device_id: DeviceId,
1099        desc: &binding_model::BindGroupDescriptor,
1100        id_in: Option<id::BindGroupId>,
1101    ) -> (id::BindGroupId, Option<binding_model::CreateBindGroupError>) {
1102        profiling::scope!("Device::create_bind_group");
1103
1104        let hub = A::hub(self);
1105        let fid = hub.bind_groups.prepare(id_in);
1106
1107        let error = loop {
1108            let device = match hub.devices.get(device_id) {
1109                Ok(device) => device,
1110                Err(_) => break DeviceError::Invalid.into(),
1111            };
1112            if !device.is_valid() {
1113                break DeviceError::Lost.into();
1114            }
1115
1116            #[cfg(feature = "trace")]
1117            if let Some(ref mut trace) = *device.trace.lock() {
1118                trace.add(trace::Action::CreateBindGroup(fid.id(), desc.clone()));
1119            }
1120
1121            let bind_group_layout = match hub.bind_group_layouts.get(desc.layout) {
1122                Ok(layout) => layout,
1123                Err(..) => break binding_model::CreateBindGroupError::InvalidLayout,
1124            };
1125
1126            if bind_group_layout.device.as_info().id() != device.as_info().id() {
1127                break DeviceError::WrongDevice.into();
1128            }
1129
1130            let bind_group = match device.create_bind_group(&bind_group_layout, desc, hub) {
1131                Ok(bind_group) => bind_group,
1132                Err(e) => break e,
1133            };
1134
1135            let (id, resource) = fid.assign(Arc::new(bind_group));
1136
1137            let weak_ref = Arc::downgrade(&resource);
1138            for range in &resource.used_texture_ranges {
1139                range.texture.bind_groups.lock().push(weak_ref.clone());
1140            }
1141            for range in &resource.used_buffer_ranges {
1142                range.buffer.bind_groups.lock().push(weak_ref.clone());
1143            }
1144
1145            api_log!("Device::create_bind_group -> {id:?}");
1146
1147            device.trackers.lock().bind_groups.insert_single(resource);
1148            return (id, None);
1149        };
1150
1151        let id = fid.assign_error(desc.label.borrow_or_default());
1152        (id, Some(error))
1153    }
1154
1155    pub fn bind_group_label<A: HalApi>(&self, id: id::BindGroupId) -> String {
1156        A::hub(self).bind_groups.label_for_resource(id)
1157    }
1158
1159    pub fn bind_group_drop<A: HalApi>(&self, bind_group_id: id::BindGroupId) {
1160        profiling::scope!("BindGroup::drop");
1161        api_log!("BindGroup::drop {bind_group_id:?}");
1162
1163        let hub = A::hub(self);
1164
1165        if let Some(bind_group) = hub.bind_groups.unregister(bind_group_id) {
1166            bind_group
1167                .device
1168                .lock_life()
1169                .suspected_resources
1170                .bind_groups
1171                .insert(bind_group.info.tracker_index(), bind_group.clone());
1172        }
1173    }
1174
1175    /// Create a shader module with the given `source`.
1176    ///
1177    /// <div class="warning">
1178    // NOTE: Keep this in sync with `naga::front::wgsl::parse_str`!
1179    // NOTE: Keep this in sync with `wgpu::Device::create_shader_module`!
1180    ///
1181    /// This function may consume a lot of stack space. Compiler-enforced limits for parsing
1182    /// recursion exist; if shader compilation runs into them, it will return an error gracefully.
1183    /// However, on some build profiles and platforms, the default stack size for a thread may be
1184    /// exceeded before this limit is reached during parsing. Callers should ensure that there is
1185    /// enough stack space for this, particularly if calls to this method are exposed to user
1186    /// input.
1187    ///
1188    /// </div>
1189    pub fn device_create_shader_module<A: HalApi>(
1190        &self,
1191        device_id: DeviceId,
1192        desc: &pipeline::ShaderModuleDescriptor,
1193        source: pipeline::ShaderModuleSource,
1194        id_in: Option<id::ShaderModuleId>,
1195    ) -> (
1196        id::ShaderModuleId,
1197        Option<pipeline::CreateShaderModuleError>,
1198    ) {
1199        profiling::scope!("Device::create_shader_module");
1200
1201        let hub = A::hub(self);
1202        let fid = hub.shader_modules.prepare(id_in);
1203
1204        let error = loop {
1205            let device = match hub.devices.get(device_id) {
1206                Ok(device) => device,
1207                Err(_) => break DeviceError::Invalid.into(),
1208            };
1209            if !device.is_valid() {
1210                break DeviceError::Lost.into();
1211            }
1212
1213            #[cfg(feature = "trace")]
1214            if let Some(ref mut trace) = *device.trace.lock() {
1215                let data = match source {
1216                    #[cfg(feature = "wgsl")]
1217                    pipeline::ShaderModuleSource::Wgsl(ref code) => {
1218                        trace.make_binary("wgsl", code.as_bytes())
1219                    }
1220                    #[cfg(feature = "glsl")]
1221                    pipeline::ShaderModuleSource::Glsl(ref code, _) => {
1222                        trace.make_binary("glsl", code.as_bytes())
1223                    }
1224                    #[cfg(feature = "spirv")]
1225                    pipeline::ShaderModuleSource::SpirV(ref code, _) => {
1226                        trace.make_binary("spirv", bytemuck::cast_slice::<u32, u8>(code))
1227                    }
1228                    pipeline::ShaderModuleSource::Naga(ref module) => {
1229                        let string =
1230                            ron::ser::to_string_pretty(module, ron::ser::PrettyConfig::default())
1231                                .unwrap();
1232                        trace.make_binary("ron", string.as_bytes())
1233                    }
1234                    pipeline::ShaderModuleSource::Dummy(_) => {
1235                        panic!("found `ShaderModuleSource::Dummy`")
1236                    }
1237                };
1238                trace.add(trace::Action::CreateShaderModule {
1239                    id: fid.id(),
1240                    desc: desc.clone(),
1241                    data,
1242                });
1243            };
1244
1245            let shader = match device.create_shader_module(desc, source) {
1246                Ok(shader) => shader,
1247                Err(e) => break e,
1248            };
1249
1250            let (id, _) = fid.assign(Arc::new(shader));
1251            api_log!("Device::create_shader_module -> {id:?}");
1252            return (id, None);
1253        };
1254
1255        log::error!("Device::create_shader_module error: {error}");
1256
1257        let id = fid.assign_error(desc.label.borrow_or_default());
1258        (id, Some(error))
1259    }
1260
1261    // Unsafe-ness of internal calls has little to do with unsafe-ness of this.
1262    #[allow(unused_unsafe)]
1263    /// # Safety
1264    ///
1265    /// This function passes SPIR-V binary to the backend as-is and can potentially result in a
1266    /// driver crash.
1267    pub unsafe fn device_create_shader_module_spirv<A: HalApi>(
1268        &self,
1269        device_id: DeviceId,
1270        desc: &pipeline::ShaderModuleDescriptor,
1271        source: Cow<[u32]>,
1272        id_in: Option<id::ShaderModuleId>,
1273    ) -> (
1274        id::ShaderModuleId,
1275        Option<pipeline::CreateShaderModuleError>,
1276    ) {
1277        profiling::scope!("Device::create_shader_module");
1278
1279        let hub = A::hub(self);
1280        let fid = hub.shader_modules.prepare(id_in);
1281
1282        let error = loop {
1283            let device = match hub.devices.get(device_id) {
1284                Ok(device) => device,
1285                Err(_) => break DeviceError::Invalid.into(),
1286            };
1287            if !device.is_valid() {
1288                break DeviceError::Lost.into();
1289            }
1290
1291            #[cfg(feature = "trace")]
1292            if let Some(ref mut trace) = *device.trace.lock() {
1293                let data = trace.make_binary("spv", unsafe {
1294                    std::slice::from_raw_parts(source.as_ptr() as *const u8, source.len() * 4)
1295                });
1296                trace.add(trace::Action::CreateShaderModule {
1297                    id: fid.id(),
1298                    desc: desc.clone(),
1299                    data,
1300                });
1301            };
1302
1303            let shader = match unsafe { device.create_shader_module_spirv(desc, &source) } {
1304                Ok(shader) => shader,
1305                Err(e) => break e,
1306            };
1307            let (id, _) = fid.assign(Arc::new(shader));
1308            api_log!("Device::create_shader_module_spirv -> {id:?}");
1309            return (id, None);
1310        };
1311
1312        log::error!("Device::create_shader_module_spirv error: {error}");
1313
1314        let id = fid.assign_error(desc.label.borrow_or_default());
1315        (id, Some(error))
1316    }
1317
1318    pub fn shader_module_label<A: HalApi>(&self, id: id::ShaderModuleId) -> String {
1319        A::hub(self).shader_modules.label_for_resource(id)
1320    }
1321
1322    pub fn shader_module_drop<A: HalApi>(&self, shader_module_id: id::ShaderModuleId) {
1323        profiling::scope!("ShaderModule::drop");
1324        api_log!("ShaderModule::drop {shader_module_id:?}");
1325
1326        let hub = A::hub(self);
1327        hub.shader_modules.unregister(shader_module_id);
1328    }
1329
1330    pub fn device_create_command_encoder<A: HalApi>(
1331        &self,
1332        device_id: DeviceId,
1333        desc: &wgt::CommandEncoderDescriptor<Label>,
1334        id_in: Option<id::CommandEncoderId>,
1335    ) -> (id::CommandEncoderId, Option<DeviceError>) {
1336        profiling::scope!("Device::create_command_encoder");
1337
1338        let hub = A::hub(self);
1339        let fid = hub
1340            .command_buffers
1341            .prepare(id_in.map(|id| id.into_command_buffer_id()));
1342
1343        let error = loop {
1344            let device = match hub.devices.get(device_id) {
1345                Ok(device) => device,
1346                Err(_) => break DeviceError::Invalid,
1347            };
1348            if !device.is_valid() {
1349                break DeviceError::Lost;
1350            }
1351            let Some(queue) = device.get_queue() else {
1352                break DeviceError::InvalidQueueId;
1353            };
1354            let encoder = match device
1355                .command_allocator
1356                .acquire_encoder(device.raw(), queue.raw.as_ref().unwrap())
1357            {
1358                Ok(raw) => raw,
1359                Err(_) => break DeviceError::OutOfMemory,
1360            };
1361            let command_buffer = command::CommandBuffer::new(
1362                encoder,
1363                &device,
1364                #[cfg(feature = "trace")]
1365                device.trace.lock().is_some(),
1366                desc.label
1367                    .to_hal(device.instance_flags)
1368                    .map(|s| s.to_string()),
1369            );
1370
1371            let (id, _) = fid.assign(Arc::new(command_buffer));
1372            api_log!("Device::create_command_encoder -> {id:?}");
1373            return (id.into_command_encoder_id(), None);
1374        };
1375
1376        let id = fid.assign_error(desc.label.borrow_or_default());
1377        (id.into_command_encoder_id(), Some(error))
1378    }
1379
1380    pub fn command_buffer_label<A: HalApi>(&self, id: id::CommandBufferId) -> String {
1381        A::hub(self).command_buffers.label_for_resource(id)
1382    }
1383
1384    pub fn command_encoder_drop<A: HalApi>(&self, command_encoder_id: id::CommandEncoderId) {
1385        profiling::scope!("CommandEncoder::drop");
1386        api_log!("CommandEncoder::drop {command_encoder_id:?}");
1387
1388        let hub = A::hub(self);
1389
1390        if let Some(cmd_buf) = hub
1391            .command_buffers
1392            .unregister(command_encoder_id.into_command_buffer_id())
1393        {
1394            cmd_buf.data.lock().as_mut().unwrap().encoder.discard();
1395            cmd_buf
1396                .device
1397                .untrack(&cmd_buf.data.lock().as_ref().unwrap().trackers);
1398        }
1399    }
1400
1401    pub fn command_buffer_drop<A: HalApi>(&self, command_buffer_id: id::CommandBufferId) {
1402        profiling::scope!("CommandBuffer::drop");
1403        api_log!("CommandBuffer::drop {command_buffer_id:?}");
1404        self.command_encoder_drop::<A>(command_buffer_id.into_command_encoder_id())
1405    }
1406
1407    pub fn device_create_render_bundle_encoder(
1408        &self,
1409        device_id: DeviceId,
1410        desc: &command::RenderBundleEncoderDescriptor,
1411    ) -> (
1412        *mut command::RenderBundleEncoder,
1413        Option<command::CreateRenderBundleError>,
1414    ) {
1415        profiling::scope!("Device::create_render_bundle_encoder");
1416        api_log!("Device::device_create_render_bundle_encoder");
1417        let (encoder, error) = match command::RenderBundleEncoder::new(desc, device_id, None) {
1418            Ok(encoder) => (encoder, None),
1419            Err(e) => (command::RenderBundleEncoder::dummy(device_id), Some(e)),
1420        };
1421        (Box::into_raw(Box::new(encoder)), error)
1422    }
1423
1424    pub fn render_bundle_encoder_finish<A: HalApi>(
1425        &self,
1426        bundle_encoder: command::RenderBundleEncoder,
1427        desc: &command::RenderBundleDescriptor,
1428        id_in: Option<id::RenderBundleId>,
1429    ) -> (id::RenderBundleId, Option<command::RenderBundleError>) {
1430        profiling::scope!("RenderBundleEncoder::finish");
1431
1432        let hub = A::hub(self);
1433
1434        let fid = hub.render_bundles.prepare(id_in);
1435
1436        let error = loop {
1437            let device = match hub.devices.get(bundle_encoder.parent()) {
1438                Ok(device) => device,
1439                Err(_) => break command::RenderBundleError::INVALID_DEVICE,
1440            };
1441            if !device.is_valid() {
1442                break command::RenderBundleError::INVALID_DEVICE;
1443            }
1444
1445            #[cfg(feature = "trace")]
1446            if let Some(ref mut trace) = *device.trace.lock() {
1447                trace.add(trace::Action::CreateRenderBundle {
1448                    id: fid.id(),
1449                    desc: trace::new_render_bundle_encoder_descriptor(
1450                        desc.label.clone(),
1451                        &bundle_encoder.context,
1452                        bundle_encoder.is_depth_read_only,
1453                        bundle_encoder.is_stencil_read_only,
1454                    ),
1455                    base: bundle_encoder.to_base_pass(),
1456                });
1457            }
1458
1459            let render_bundle = match bundle_encoder.finish(desc, &device, hub) {
1460                Ok(bundle) => bundle,
1461                Err(e) => break e,
1462            };
1463
1464            let (id, resource) = fid.assign(Arc::new(render_bundle));
1465            api_log!("RenderBundleEncoder::finish -> {id:?}");
1466            device.trackers.lock().bundles.insert_single(resource);
1467            return (id, None);
1468        };
1469
1470        let id = fid.assign_error(desc.label.borrow_or_default());
1471        (id, Some(error))
1472    }
1473
1474    pub fn render_bundle_label<A: HalApi>(&self, id: id::RenderBundleId) -> String {
1475        A::hub(self).render_bundles.label_for_resource(id)
1476    }
1477
1478    pub fn render_bundle_drop<A: HalApi>(&self, render_bundle_id: id::RenderBundleId) {
1479        profiling::scope!("RenderBundle::drop");
1480        api_log!("RenderBundle::drop {render_bundle_id:?}");
1481
1482        let hub = A::hub(self);
1483
1484        if let Some(bundle) = hub.render_bundles.unregister(render_bundle_id) {
1485            bundle
1486                .device
1487                .lock_life()
1488                .suspected_resources
1489                .render_bundles
1490                .insert(bundle.info.tracker_index(), bundle.clone());
1491        }
1492    }
1493
1494    pub fn device_create_query_set<A: HalApi>(
1495        &self,
1496        device_id: DeviceId,
1497        desc: &resource::QuerySetDescriptor,
1498        id_in: Option<id::QuerySetId>,
1499    ) -> (id::QuerySetId, Option<resource::CreateQuerySetError>) {
1500        profiling::scope!("Device::create_query_set");
1501
1502        let hub = A::hub(self);
1503        let fid = hub.query_sets.prepare(id_in);
1504
1505        let error = loop {
1506            let device = match hub.devices.get(device_id) {
1507                Ok(device) => device,
1508                Err(_) => break DeviceError::Invalid.into(),
1509            };
1510            if !device.is_valid() {
1511                break DeviceError::Lost.into();
1512            }
1513
1514            #[cfg(feature = "trace")]
1515            if let Some(ref mut trace) = *device.trace.lock() {
1516                trace.add(trace::Action::CreateQuerySet {
1517                    id: fid.id(),
1518                    desc: desc.clone(),
1519                });
1520            }
1521
1522            let query_set = match device.create_query_set(desc) {
1523                Ok(query_set) => query_set,
1524                Err(err) => break err,
1525            };
1526
1527            let (id, resource) = fid.assign(Arc::new(query_set));
1528            api_log!("Device::create_query_set -> {id:?}");
1529            device.trackers.lock().query_sets.insert_single(resource);
1530
1531            return (id, None);
1532        };
1533
1534        let id = fid.assign_error("");
1535        (id, Some(error))
1536    }
1537
1538    pub fn query_set_drop<A: HalApi>(&self, query_set_id: id::QuerySetId) {
1539        profiling::scope!("QuerySet::drop");
1540        api_log!("QuerySet::drop {query_set_id:?}");
1541
1542        let hub = A::hub(self);
1543
1544        if let Some(query_set) = hub.query_sets.unregister(query_set_id) {
1545            let device = &query_set.device;
1546
1547            #[cfg(feature = "trace")]
1548            if let Some(ref mut trace) = *device.trace.lock() {
1549                trace.add(trace::Action::DestroyQuerySet(query_set_id));
1550            }
1551
1552            device
1553                .lock_life()
1554                .suspected_resources
1555                .query_sets
1556                .insert(query_set.info.tracker_index(), query_set.clone());
1557        }
1558    }
1559
1560    pub fn query_set_label<A: HalApi>(&self, id: id::QuerySetId) -> String {
1561        A::hub(self).query_sets.label_for_resource(id)
1562    }
1563
1564    pub fn device_create_render_pipeline<A: HalApi>(
1565        &self,
1566        device_id: DeviceId,
1567        desc: &pipeline::RenderPipelineDescriptor,
1568        id_in: Option<id::RenderPipelineId>,
1569        implicit_pipeline_ids: Option<ImplicitPipelineIds<'_>>,
1570    ) -> (
1571        id::RenderPipelineId,
1572        Option<pipeline::CreateRenderPipelineError>,
1573    ) {
1574        profiling::scope!("Device::create_render_pipeline");
1575
1576        let hub = A::hub(self);
1577
1578        let fid = hub.render_pipelines.prepare(id_in);
1579        let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(hub));
1580        let implicit_error_context = implicit_context.clone();
1581
1582        let error = loop {
1583            let device = match hub.devices.get(device_id) {
1584                Ok(device) => device,
1585                Err(_) => break DeviceError::Invalid.into(),
1586            };
1587            if !device.is_valid() {
1588                break DeviceError::Lost.into();
1589            }
1590            #[cfg(feature = "trace")]
1591            if let Some(ref mut trace) = *device.trace.lock() {
1592                trace.add(trace::Action::CreateRenderPipeline {
1593                    id: fid.id(),
1594                    desc: desc.clone(),
1595                    implicit_context: implicit_context.clone(),
1596                });
1597            }
1598
1599            let pipeline =
1600                match device.create_render_pipeline(&device.adapter, desc, implicit_context, hub) {
1601                    Ok(pair) => pair,
1602                    Err(e) => break e,
1603                };
1604
1605            let (id, resource) = fid.assign(Arc::new(pipeline));
1606            api_log!("Device::create_render_pipeline -> {id:?}");
1607
1608            device
1609                .trackers
1610                .lock()
1611                .render_pipelines
1612                .insert_single(resource);
1613
1614            return (id, None);
1615        };
1616
1617        let id = fid.assign_error(desc.label.borrow_or_default());
1618
1619        // We also need to assign errors to the implicit pipeline layout and the
1620        // implicit bind group layout. We have to remove any existing entries first.
1621        let mut pipeline_layout_guard = hub.pipeline_layouts.write();
1622        let mut bgl_guard = hub.bind_group_layouts.write();
1623        if let Some(ref ids) = implicit_error_context {
1624            if pipeline_layout_guard.contains(ids.root_id) {
1625                pipeline_layout_guard.remove(ids.root_id);
1626            }
1627            pipeline_layout_guard.insert_error(ids.root_id, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL);
1628            for &bgl_id in ids.group_ids.iter() {
1629                if bgl_guard.contains(bgl_id) {
1630                    bgl_guard.remove(bgl_id);
1631                }
1632                bgl_guard.insert_error(bgl_id, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL);
1633            }
1634        }
1635
1636        log::error!("Device::create_render_pipeline error: {error}");
1637
1638        (id, Some(error))
1639    }
1640
1641    /// Get an ID of one of the bind group layouts. The ID adds a refcount,
1642    /// which needs to be released by calling `bind_group_layout_drop`.
1643    pub fn render_pipeline_get_bind_group_layout<A: HalApi>(
1644        &self,
1645        pipeline_id: id::RenderPipelineId,
1646        index: u32,
1647        id_in: Option<id::BindGroupLayoutId>,
1648    ) -> (
1649        id::BindGroupLayoutId,
1650        Option<binding_model::GetBindGroupLayoutError>,
1651    ) {
1652        let hub = A::hub(self);
1653
1654        let error = loop {
1655            let pipeline = match hub.render_pipelines.get(pipeline_id) {
1656                Ok(pipeline) => pipeline,
1657                Err(_) => break binding_model::GetBindGroupLayoutError::InvalidPipeline,
1658            };
1659            let id = match pipeline.layout.bind_group_layouts.get(index as usize) {
1660                Some(bg) => hub.bind_group_layouts.prepare(id_in).assign_existing(bg),
1661                None => break binding_model::GetBindGroupLayoutError::InvalidGroupIndex(index),
1662            };
1663            return (id, None);
1664        };
1665
1666        let id = hub
1667            .bind_group_layouts
1668            .prepare(id_in)
1669            .assign_error("<derived>");
1670        (id, Some(error))
1671    }
1672
1673    pub fn render_pipeline_label<A: HalApi>(&self, id: id::RenderPipelineId) -> String {
1674        A::hub(self).render_pipelines.label_for_resource(id)
1675    }
1676
1677    pub fn render_pipeline_drop<A: HalApi>(&self, render_pipeline_id: id::RenderPipelineId) {
1678        profiling::scope!("RenderPipeline::drop");
1679        api_log!("RenderPipeline::drop {render_pipeline_id:?}");
1680
1681        let hub = A::hub(self);
1682
1683        if let Some(pipeline) = hub.render_pipelines.unregister(render_pipeline_id) {
1684            let device = &pipeline.device;
1685            let mut life_lock = device.lock_life();
1686            life_lock
1687                .suspected_resources
1688                .render_pipelines
1689                .insert(pipeline.info.tracker_index(), pipeline.clone());
1690
1691            life_lock.suspected_resources.pipeline_layouts.insert(
1692                pipeline.layout.info.tracker_index(),
1693                pipeline.layout.clone(),
1694            );
1695        }
1696    }
1697
1698    pub fn device_create_compute_pipeline<A: HalApi>(
1699        &self,
1700        device_id: DeviceId,
1701        desc: &pipeline::ComputePipelineDescriptor,
1702        id_in: Option<id::ComputePipelineId>,
1703        implicit_pipeline_ids: Option<ImplicitPipelineIds<'_>>,
1704    ) -> (
1705        id::ComputePipelineId,
1706        Option<pipeline::CreateComputePipelineError>,
1707    ) {
1708        profiling::scope!("Device::create_compute_pipeline");
1709
1710        let hub = A::hub(self);
1711
1712        let fid = hub.compute_pipelines.prepare(id_in);
1713        let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(hub));
1714        let implicit_error_context = implicit_context.clone();
1715
1716        let error = loop {
1717            let device = match hub.devices.get(device_id) {
1718                Ok(device) => device,
1719                Err(_) => break DeviceError::Invalid.into(),
1720            };
1721            if !device.is_valid() {
1722                break DeviceError::Lost.into();
1723            }
1724
1725            #[cfg(feature = "trace")]
1726            if let Some(ref mut trace) = *device.trace.lock() {
1727                trace.add(trace::Action::CreateComputePipeline {
1728                    id: fid.id(),
1729                    desc: desc.clone(),
1730                    implicit_context: implicit_context.clone(),
1731                });
1732            }
1733            let pipeline = match device.create_compute_pipeline(desc, implicit_context, hub) {
1734                Ok(pair) => pair,
1735                Err(e) => break e,
1736            };
1737
1738            let (id, resource) = fid.assign(Arc::new(pipeline));
1739            api_log!("Device::create_compute_pipeline -> {id:?}");
1740
1741            device
1742                .trackers
1743                .lock()
1744                .compute_pipelines
1745                .insert_single(resource);
1746            return (id, None);
1747        };
1748
1749        let id = fid.assign_error(desc.label.borrow_or_default());
1750
1751        // We also need to assign errors to the implicit pipeline layout and the
1752        // implicit bind group layout. We have to remove any existing entries first.
1753        let mut pipeline_layout_guard = hub.pipeline_layouts.write();
1754        let mut bgl_guard = hub.bind_group_layouts.write();
1755        if let Some(ref ids) = implicit_error_context {
1756            if pipeline_layout_guard.contains(ids.root_id) {
1757                pipeline_layout_guard.remove(ids.root_id);
1758            }
1759            pipeline_layout_guard.insert_error(ids.root_id, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL);
1760            for &bgl_id in ids.group_ids.iter() {
1761                if bgl_guard.contains(bgl_id) {
1762                    bgl_guard.remove(bgl_id);
1763                }
1764                bgl_guard.insert_error(bgl_id, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL);
1765            }
1766        }
1767        (id, Some(error))
1768    }
1769
1770    /// Get an ID of one of the bind group layouts. The ID adds a refcount,
1771    /// which needs to be released by calling `bind_group_layout_drop`.
1772    pub fn compute_pipeline_get_bind_group_layout<A: HalApi>(
1773        &self,
1774        pipeline_id: id::ComputePipelineId,
1775        index: u32,
1776        id_in: Option<id::BindGroupLayoutId>,
1777    ) -> (
1778        id::BindGroupLayoutId,
1779        Option<binding_model::GetBindGroupLayoutError>,
1780    ) {
1781        let hub = A::hub(self);
1782
1783        let error = loop {
1784            let pipeline = match hub.compute_pipelines.get(pipeline_id) {
1785                Ok(pipeline) => pipeline,
1786                Err(_) => break binding_model::GetBindGroupLayoutError::InvalidPipeline,
1787            };
1788
1789            let id = match pipeline.layout.bind_group_layouts.get(index as usize) {
1790                Some(bg) => hub.bind_group_layouts.prepare(id_in).assign_existing(bg),
1791                None => break binding_model::GetBindGroupLayoutError::InvalidGroupIndex(index),
1792            };
1793
1794            return (id, None);
1795        };
1796
1797        let id = hub
1798            .bind_group_layouts
1799            .prepare(id_in)
1800            .assign_error("<derived>");
1801        (id, Some(error))
1802    }
1803
1804    pub fn compute_pipeline_label<A: HalApi>(&self, id: id::ComputePipelineId) -> String {
1805        A::hub(self).compute_pipelines.label_for_resource(id)
1806    }
1807
1808    pub fn compute_pipeline_drop<A: HalApi>(&self, compute_pipeline_id: id::ComputePipelineId) {
1809        profiling::scope!("ComputePipeline::drop");
1810        api_log!("ComputePipeline::drop {compute_pipeline_id:?}");
1811
1812        let hub = A::hub(self);
1813
1814        if let Some(pipeline) = hub.compute_pipelines.unregister(compute_pipeline_id) {
1815            let device = &pipeline.device;
1816            let mut life_lock = device.lock_life();
1817            life_lock
1818                .suspected_resources
1819                .compute_pipelines
1820                .insert(pipeline.info.tracker_index(), pipeline.clone());
1821            life_lock.suspected_resources.pipeline_layouts.insert(
1822                pipeline.layout.info.tracker_index(),
1823                pipeline.layout.clone(),
1824            );
1825        }
1826    }
1827
1828    pub fn surface_configure<A: HalApi>(
1829        &self,
1830        surface_id: SurfaceId,
1831        device_id: DeviceId,
1832        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
1833    ) -> Option<present::ConfigureSurfaceError> {
1834        use hal::{Adapter as _, Surface as _};
1835        use present::ConfigureSurfaceError as E;
1836        profiling::scope!("surface_configure");
1837
1838        fn validate_surface_configuration(
1839            config: &mut hal::SurfaceConfiguration,
1840            caps: &hal::SurfaceCapabilities,
1841            max_texture_dimension_2d: u32,
1842        ) -> Result<(), E> {
1843            let width = config.extent.width;
1844            let height = config.extent.height;
1845
1846            if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
1847                return Err(E::TooLarge {
1848                    width,
1849                    height,
1850                    max_texture_dimension_2d,
1851                });
1852            }
1853
1854            if !caps.present_modes.contains(&config.present_mode) {
1855                let new_mode = 'b: loop {
1856                    // Automatic present mode checks.
1857                    //
1858                    // The "Automatic" modes are never supported by the backends.
1859                    let fallbacks = match config.present_mode {
1860                        wgt::PresentMode::AutoVsync => {
1861                            &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]
1862                        }
1863                        // Always end in FIFO to make sure it's always supported
1864                        wgt::PresentMode::AutoNoVsync => &[
1865                            wgt::PresentMode::Immediate,
1866                            wgt::PresentMode::Mailbox,
1867                            wgt::PresentMode::Fifo,
1868                        ][..],
1869                        _ => {
1870                            return Err(E::UnsupportedPresentMode {
1871                                requested: config.present_mode,
1872                                available: caps.present_modes.clone(),
1873                            });
1874                        }
1875                    };
1876
1877                    for &fallback in fallbacks {
1878                        if caps.present_modes.contains(&fallback) {
1879                            break 'b fallback;
1880                        }
1881                    }
1882
1883                    unreachable!("Fallback system failed to choose present mode. This is a bug. Mode: {:?}, Options: {:?}", config.present_mode, &caps.present_modes);
1884                };
1885
1886                api_log!(
1887                    "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
1888                    config.present_mode
1889                );
1890                config.present_mode = new_mode;
1891            }
1892            if !caps.formats.contains(&config.format) {
1893                return Err(E::UnsupportedFormat {
1894                    requested: config.format,
1895                    available: caps.formats.clone(),
1896                });
1897            }
1898            if !caps
1899                .composite_alpha_modes
1900                .contains(&config.composite_alpha_mode)
1901            {
1902                let new_alpha_mode = 'alpha: loop {
1903                    // Automatic alpha mode checks.
1904                    let fallbacks = match config.composite_alpha_mode {
1905                        wgt::CompositeAlphaMode::Auto => &[
1906                            wgt::CompositeAlphaMode::Opaque,
1907                            wgt::CompositeAlphaMode::Inherit,
1908                        ][..],
1909                        _ => {
1910                            return Err(E::UnsupportedAlphaMode {
1911                                requested: config.composite_alpha_mode,
1912                                available: caps.composite_alpha_modes.clone(),
1913                            });
1914                        }
1915                    };
1916
1917                    for &fallback in fallbacks {
1918                        if caps.composite_alpha_modes.contains(&fallback) {
1919                            break 'alpha fallback;
1920                        }
1921                    }
1922
1923                    unreachable!(
1924                        "Fallback system failed to choose alpha mode. This is a bug. \
1925                                  AlphaMode: {:?}, Options: {:?}",
1926                        config.composite_alpha_mode, &caps.composite_alpha_modes
1927                    );
1928                };
1929
1930                api_log!(
1931                    "Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
1932                    config.composite_alpha_mode
1933                );
1934                config.composite_alpha_mode = new_alpha_mode;
1935            }
1936            if !caps.usage.contains(config.usage) {
1937                return Err(E::UnsupportedUsage);
1938            }
1939            if width == 0 || height == 0 {
1940                return Err(E::ZeroArea);
1941            }
1942            Ok(())
1943        }
1944
1945        log::debug!("configuring surface with {:?}", config);
1946
1947        let error = 'outer: loop {
1948            // User callbacks must not be called while we are holding locks.
1949            let user_callbacks;
1950            {
1951                let hub = A::hub(self);
1952                let surface_guard = self.surfaces.read();
1953                let device_guard = hub.devices.read();
1954
1955                let device = match device_guard.get(device_id) {
1956                    Ok(device) => device,
1957                    Err(_) => break DeviceError::Invalid.into(),
1958                };
1959                if !device.is_valid() {
1960                    break DeviceError::Lost.into();
1961                }
1962
1963                #[cfg(feature = "trace")]
1964                if let Some(ref mut trace) = *device.trace.lock() {
1965                    trace.add(trace::Action::ConfigureSurface(surface_id, config.clone()));
1966                }
1967
1968                let surface = match surface_guard.get(surface_id) {
1969                    Ok(surface) => surface,
1970                    Err(_) => break E::InvalidSurface,
1971                };
1972
1973                let caps = unsafe {
1974                    let suf = A::surface_as_hal(surface);
1975                    let adapter = &device.adapter;
1976                    match adapter.raw.adapter.surface_capabilities(suf.unwrap()) {
1977                        Some(caps) => caps,
1978                        None => break E::UnsupportedQueueFamily,
1979                    }
1980                };
1981
1982                let mut hal_view_formats = vec![];
1983                for format in config.view_formats.iter() {
1984                    if *format == config.format {
1985                        continue;
1986                    }
1987                    if !caps.formats.contains(&config.format) {
1988                        break 'outer E::UnsupportedFormat {
1989                            requested: config.format,
1990                            available: caps.formats,
1991                        };
1992                    }
1993                    if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
1994                        break 'outer E::InvalidViewFormat(*format, config.format);
1995                    }
1996                    hal_view_formats.push(*format);
1997                }
1998
1999                if !hal_view_formats.is_empty() {
2000                    if let Err(missing_flag) =
2001                        device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
2002                    {
2003                        break 'outer E::MissingDownlevelFlags(missing_flag);
2004                    }
2005                }
2006
2007                let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
2008                    *caps.maximum_frame_latency.start(),
2009                    *caps.maximum_frame_latency.end(),
2010                );
2011                let mut hal_config = hal::SurfaceConfiguration {
2012                    maximum_frame_latency,
2013                    present_mode: config.present_mode,
2014                    composite_alpha_mode: config.alpha_mode,
2015                    format: config.format,
2016                    extent: wgt::Extent3d {
2017                        width: config.width,
2018                        height: config.height,
2019                        depth_or_array_layers: 1,
2020                    },
2021                    usage: conv::map_texture_usage(config.usage, hal::FormatAspects::COLOR),
2022                    view_formats: hal_view_formats,
2023                };
2024
2025                if let Err(error) = validate_surface_configuration(
2026                    &mut hal_config,
2027                    &caps,
2028                    device.limits.max_texture_dimension_2d,
2029                ) {
2030                    break error;
2031                }
2032
2033                // Wait for all work to finish before configuring the surface.
2034                let snatch_guard = device.snatchable_lock.read();
2035                let fence = device.fence.read();
2036                let fence = fence.as_ref().unwrap();
2037                match device.maintain(fence, wgt::Maintain::Wait, snatch_guard) {
2038                    Ok((closures, _)) => {
2039                        user_callbacks = closures;
2040                    }
2041                    Err(e) => {
2042                        break e.into();
2043                    }
2044                }
2045
2046                // All textures must be destroyed before the surface can be re-configured.
2047                if let Some(present) = surface.presentation.lock().take() {
2048                    if present.acquired_texture.is_some() {
2049                        break E::PreviousOutputExists;
2050                    }
2051                }
2052
2053                // TODO: Texture views may still be alive that point to the texture.
2054                // this will allow the user to render to the surface texture, long after
2055                // it has been removed.
2056                //
2057                // https://github.com/gfx-rs/wgpu/issues/4105
2058
2059                match unsafe {
2060                    A::surface_as_hal(surface)
2061                        .unwrap()
2062                        .configure(device.raw(), &hal_config)
2063                } {
2064                    Ok(()) => (),
2065                    Err(error) => {
2066                        break match error {
2067                            hal::SurfaceError::Outdated | hal::SurfaceError::Lost => {
2068                                E::InvalidSurface
2069                            }
2070                            hal::SurfaceError::Device(error) => E::Device(error.into()),
2071                            hal::SurfaceError::Other(message) => {
2072                                log::error!("surface configuration failed: {}", message);
2073                                E::InvalidSurface
2074                            }
2075                        }
2076                    }
2077                }
2078
2079                let mut presentation = surface.presentation.lock();
2080                *presentation = Some(present::Presentation {
2081                    device: super::any_device::AnyDevice::new(device.clone()),
2082                    config: config.clone(),
2083                    acquired_texture: None,
2084                });
2085            }
2086
2087            user_callbacks.fire();
2088            return None;
2089        };
2090
2091        Some(error)
2092    }
2093
2094    #[cfg(feature = "replay")]
2095    /// Only triangle suspected resource IDs. This helps us to avoid ID collisions
2096    /// upon creating new resources when re-playing a trace.
2097    pub fn device_maintain_ids<A: HalApi>(&self, device_id: DeviceId) -> Result<(), InvalidDevice> {
2098        let hub = A::hub(self);
2099
2100        let device = hub.devices.get(device_id).map_err(|_| InvalidDevice)?;
2101        if !device.is_valid() {
2102            return Err(InvalidDevice);
2103        }
2104        device.lock_life().triage_suspected(&device.trackers);
2105        Ok(())
2106    }
2107
2108    /// Check `device_id` for freeable resources and completed buffer mappings.
2109    ///
2110    /// Return `queue_empty` indicating whether there are more queue submissions still in flight.
2111    pub fn device_poll<A: HalApi>(
2112        &self,
2113        device_id: DeviceId,
2114        maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
2115    ) -> Result<bool, WaitIdleError> {
2116        api_log!("Device::poll");
2117
2118        let hub = A::hub(self);
2119        let device = hub
2120            .devices
2121            .get(device_id)
2122            .map_err(|_| DeviceError::Invalid)?;
2123
2124        if let wgt::Maintain::WaitForSubmissionIndex(submission_index) = maintain {
2125            if submission_index.queue_id != device_id.into_queue_id() {
2126                return Err(WaitIdleError::WrongSubmissionIndex(
2127                    submission_index.queue_id,
2128                    device_id,
2129                ));
2130            }
2131        }
2132
2133        let DevicePoll {
2134            closures,
2135            queue_empty,
2136        } = Self::poll_single_device(&device, maintain)?;
2137
2138        closures.fire();
2139
2140        Ok(queue_empty)
2141    }
2142
2143    fn poll_single_device<A: HalApi>(
2144        device: &crate::device::Device<A>,
2145        maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
2146    ) -> Result<DevicePoll, WaitIdleError> {
2147        let snatch_guard = device.snatchable_lock.read();
2148        let fence = device.fence.read();
2149        let fence = fence.as_ref().unwrap();
2150        let (closures, queue_empty) = device.maintain(fence, maintain, snatch_guard)?;
2151
2152        // Some deferred destroys are scheduled in maintain so run this right after
2153        // to avoid holding on to them until the next device poll.
2154        device.deferred_resource_destruction();
2155
2156        Ok(DevicePoll {
2157            closures,
2158            queue_empty,
2159        })
2160    }
2161
2162    /// Poll all devices belonging to the backend `A`.
2163    ///
2164    /// If `force_wait` is true, block until all buffer mappings are done.
2165    ///
2166    /// Return `all_queue_empty` indicating whether there are more queue
2167    /// submissions still in flight.
2168    fn poll_all_devices_of_api<A: HalApi>(
2169        &self,
2170        force_wait: bool,
2171        closures: &mut UserClosures,
2172    ) -> Result<bool, WaitIdleError> {
2173        profiling::scope!("poll_device");
2174
2175        let hub = A::hub(self);
2176        let mut all_queue_empty = true;
2177        {
2178            let device_guard = hub.devices.read();
2179
2180            for (_id, device) in device_guard.iter(A::VARIANT) {
2181                let maintain = if force_wait {
2182                    wgt::Maintain::Wait
2183                } else {
2184                    wgt::Maintain::Poll
2185                };
2186
2187                let DevicePoll {
2188                    closures: cbs,
2189                    queue_empty,
2190                } = Self::poll_single_device(device, maintain)?;
2191
2192                all_queue_empty &= queue_empty;
2193
2194                closures.extend(cbs);
2195            }
2196        }
2197
2198        Ok(all_queue_empty)
2199    }
2200
2201    /// Poll all devices on all backends.
2202    ///
2203    /// This is the implementation of `wgpu::Instance::poll_all`.
2204    ///
2205    /// Return `all_queue_empty` indicating whether there are more queue
2206    /// submissions still in flight.
2207    pub fn poll_all_devices(&self, force_wait: bool) -> Result<bool, WaitIdleError> {
2208        api_log!("poll_all_devices");
2209        let mut closures = UserClosures::default();
2210        let mut all_queue_empty = true;
2211
2212        #[cfg(vulkan)]
2213        {
2214            all_queue_empty &=
2215                self.poll_all_devices_of_api::<hal::api::Vulkan>(force_wait, &mut closures)?;
2216        }
2217        #[cfg(metal)]
2218        {
2219            all_queue_empty &=
2220                self.poll_all_devices_of_api::<hal::api::Metal>(force_wait, &mut closures)?;
2221        }
2222        #[cfg(dx12)]
2223        {
2224            all_queue_empty &=
2225                self.poll_all_devices_of_api::<hal::api::Dx12>(force_wait, &mut closures)?;
2226        }
2227        #[cfg(gles)]
2228        {
2229            all_queue_empty &=
2230                self.poll_all_devices_of_api::<hal::api::Gles>(force_wait, &mut closures)?;
2231        }
2232
2233        closures.fire();
2234
2235        Ok(all_queue_empty)
2236    }
2237
2238    pub fn device_label<A: HalApi>(&self, id: DeviceId) -> String {
2239        A::hub(self).devices.label_for_resource(id)
2240    }
2241
2242    pub fn device_start_capture<A: HalApi>(&self, id: DeviceId) {
2243        api_log!("Device::start_capture");
2244
2245        let hub = A::hub(self);
2246
2247        if let Ok(device) = hub.devices.get(id) {
2248            if !device.is_valid() {
2249                return;
2250            }
2251            unsafe { device.raw().start_capture() };
2252        }
2253    }
2254
2255    pub fn device_stop_capture<A: HalApi>(&self, id: DeviceId) {
2256        api_log!("Device::stop_capture");
2257
2258        let hub = A::hub(self);
2259
2260        if let Ok(device) = hub.devices.get(id) {
2261            if !device.is_valid() {
2262                return;
2263            }
2264            unsafe { device.raw().stop_capture() };
2265        }
2266    }
2267
2268    // This is a test-only function to force the device into an
2269    // invalid state by inserting an error value in its place in
2270    // the registry.
2271    pub fn device_make_invalid<A: HalApi>(&self, device_id: DeviceId) {
2272        let hub = A::hub(self);
2273        hub.devices
2274            .force_replace_with_error(device_id, "Made invalid.");
2275    }
2276
2277    pub fn device_drop<A: HalApi>(&self, device_id: DeviceId) {
2278        profiling::scope!("Device::drop");
2279        api_log!("Device::drop {device_id:?}");
2280
2281        let hub = A::hub(self);
2282        if let Some(device) = hub.devices.unregister(device_id) {
2283            let device_lost_closure = device.lock_life().device_lost_closure.take();
2284            if let Some(closure) = device_lost_closure {
2285                closure.call(DeviceLostReason::Dropped, String::from("Device dropped."));
2286            }
2287
2288            // The things `Device::prepare_to_die` takes care are mostly
2289            // unnecessary here. We know our queue is empty, so we don't
2290            // need to wait for submissions or triage them. We know we were
2291            // just polled, so `life_tracker.free_resources` is empty.
2292            debug_assert!(device.lock_life().queue_empty());
2293            {
2294                let mut pending_writes = device.pending_writes.lock();
2295                let pending_writes = pending_writes.as_mut().unwrap();
2296                pending_writes.deactivate();
2297            }
2298
2299            drop(device);
2300        }
2301    }
2302
2303    // This closure will be called exactly once during "lose the device",
2304    // or when it is replaced.
2305    pub fn device_set_device_lost_closure<A: HalApi>(
2306        &self,
2307        device_id: DeviceId,
2308        device_lost_closure: DeviceLostClosure,
2309    ) {
2310        let hub = A::hub(self);
2311
2312        if let Ok(Some(device)) = hub.devices.try_get(device_id) {
2313            let mut life_tracker = device.lock_life();
2314            if let Some(existing_closure) = life_tracker.device_lost_closure.take() {
2315                // It's important to not hold the lock while calling the closure.
2316                drop(life_tracker);
2317                existing_closure.call(DeviceLostReason::ReplacedCallback, "".to_string());
2318                life_tracker = device.lock_life();
2319            }
2320            life_tracker.device_lost_closure = Some(device_lost_closure);
2321        } else {
2322            // No device? Okay. Just like we have to call any existing closure
2323            // before we drop it, we need to call this closure before we exit
2324            // this function, because there's no device that is ever going to
2325            // call it.
2326            device_lost_closure.call(DeviceLostReason::DeviceInvalid, "".to_string());
2327        }
2328    }
2329
2330    pub fn device_destroy<A: HalApi>(&self, device_id: DeviceId) {
2331        api_log!("Device::destroy {device_id:?}");
2332
2333        let hub = A::hub(self);
2334
2335        if let Ok(device) = hub.devices.get(device_id) {
2336            // Follow the steps at
2337            // https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy.
2338            // It's legal to call destroy multiple times, but if the device
2339            // is already invalid, there's nothing more to do. There's also
2340            // no need to return an error.
2341            if !device.is_valid() {
2342                return;
2343            }
2344
2345            // The last part of destroy is to lose the device. The spec says
2346            // delay that until all "currently-enqueued operations on any
2347            // queue on this device are completed." This is accomplished by
2348            // setting valid to false, and then relying upon maintain to
2349            // check for empty queues and a DeviceLostClosure. At that time,
2350            // the DeviceLostClosure will be called with "destroyed" as the
2351            // reason.
2352            device.valid.store(false, Ordering::Relaxed);
2353        }
2354    }
2355
2356    pub fn device_mark_lost<A: HalApi>(&self, device_id: DeviceId, message: &str) {
2357        api_log!("Device::mark_lost {device_id:?}");
2358
2359        let hub = A::hub(self);
2360
2361        if let Ok(device) = hub.devices.get(device_id) {
2362            device.lose(message);
2363        }
2364    }
2365
2366    pub fn queue_drop<A: HalApi>(&self, queue_id: QueueId) {
2367        profiling::scope!("Queue::drop");
2368        api_log!("Queue::drop {queue_id:?}");
2369
2370        let hub = A::hub(self);
2371        if let Some(queue) = hub.queues.unregister(queue_id) {
2372            drop(queue);
2373        }
2374    }
2375
2376    pub fn buffer_map_async<A: HalApi>(
2377        &self,
2378        buffer_id: id::BufferId,
2379        offset: BufferAddress,
2380        size: Option<BufferAddress>,
2381        op: BufferMapOperation,
2382    ) -> BufferAccessResult {
2383        api_log!("Buffer::map_async {buffer_id:?} offset {offset:?} size {size:?} op: {op:?}");
2384
2385        // User callbacks must not be called while holding buffer_map_async_inner's locks, so we
2386        // defer the error callback if it needs to be called immediately (typically when running
2387        // into errors).
2388        if let Err((mut operation, err)) =
2389            self.buffer_map_async_inner::<A>(buffer_id, offset, size, op)
2390        {
2391            if let Some(callback) = operation.callback.take() {
2392                callback.call(Err(err.clone()));
2393            }
2394            log::error!("Buffer::map_async error: {err}");
2395            return Err(err);
2396        }
2397
2398        Ok(())
2399    }
2400
2401    // Returns the mapping callback in case of error so that the callback can be fired outside
2402    // of the locks that are held in this function.
2403    fn buffer_map_async_inner<A: HalApi>(
2404        &self,
2405        buffer_id: id::BufferId,
2406        offset: BufferAddress,
2407        size: Option<BufferAddress>,
2408        op: BufferMapOperation,
2409    ) -> Result<(), (BufferMapOperation, BufferAccessError)> {
2410        profiling::scope!("Buffer::map_async");
2411
2412        let hub = A::hub(self);
2413
2414        let (pub_usage, internal_use) = match op.host {
2415            HostMap::Read => (wgt::BufferUsages::MAP_READ, hal::BufferUses::MAP_READ),
2416            HostMap::Write => (wgt::BufferUsages::MAP_WRITE, hal::BufferUses::MAP_WRITE),
2417        };
2418
2419        let buffer = {
2420            let buffer = hub.buffers.get(buffer_id);
2421
2422            let buffer = match buffer {
2423                Ok(b) => b,
2424                Err(_) => {
2425                    return Err((op, BufferAccessError::Invalid));
2426                }
2427            };
2428            {
2429                let snatch_guard = buffer.device.snatchable_lock.read();
2430                if buffer.is_destroyed(&snatch_guard) {
2431                    return Err((op, BufferAccessError::Destroyed));
2432                }
2433            }
2434
2435            let range_size = if let Some(size) = size {
2436                size
2437            } else if offset > buffer.size {
2438                0
2439            } else {
2440                buffer.size - offset
2441            };
2442
2443            if offset % wgt::MAP_ALIGNMENT != 0 {
2444                return Err((op, BufferAccessError::UnalignedOffset { offset }));
2445            }
2446            if range_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
2447                return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
2448            }
2449
2450            let range = offset..(offset + range_size);
2451
2452            if range.start % wgt::MAP_ALIGNMENT != 0 || range.end % wgt::COPY_BUFFER_ALIGNMENT != 0
2453            {
2454                return Err((op, BufferAccessError::UnalignedRange));
2455            }
2456
2457            let device = &buffer.device;
2458            if !device.is_valid() {
2459                return Err((op, DeviceError::Lost.into()));
2460            }
2461
2462            if let Err(e) = check_buffer_usage(buffer.info.id(), buffer.usage, pub_usage) {
2463                return Err((op, e.into()));
2464            }
2465
2466            if range.start > range.end {
2467                return Err((
2468                    op,
2469                    BufferAccessError::NegativeRange {
2470                        start: range.start,
2471                        end: range.end,
2472                    },
2473                ));
2474            }
2475            if range.end > buffer.size {
2476                return Err((
2477                    op,
2478                    BufferAccessError::OutOfBoundsOverrun {
2479                        index: range.end,
2480                        max: buffer.size,
2481                    },
2482                ));
2483            }
2484
2485            {
2486                let map_state = &mut *buffer.map_state.lock();
2487                *map_state = match *map_state {
2488                    resource::BufferMapState::Init { .. }
2489                    | resource::BufferMapState::Active { .. } => {
2490                        return Err((op, BufferAccessError::AlreadyMapped));
2491                    }
2492                    resource::BufferMapState::Waiting(_) => {
2493                        return Err((op, BufferAccessError::MapAlreadyPending));
2494                    }
2495                    resource::BufferMapState::Idle => {
2496                        resource::BufferMapState::Waiting(resource::BufferPendingMapping {
2497                            range,
2498                            op,
2499                            _parent_buffer: buffer.clone(),
2500                        })
2501                    }
2502                };
2503            }
2504
2505            let snatch_guard = buffer.device.snatchable_lock.read();
2506
2507            {
2508                let mut trackers = buffer.device.as_ref().trackers.lock();
2509                trackers.buffers.set_single(&buffer, internal_use);
2510                //TODO: Check if draining ALL buffers is correct!
2511                let _ = trackers.buffers.drain_transitions(&snatch_guard);
2512            }
2513
2514            drop(snatch_guard);
2515
2516            buffer
2517        };
2518
2519        buffer.device.lock_life().map(&buffer);
2520
2521        Ok(())
2522    }
2523
2524    pub fn buffer_get_mapped_range<A: HalApi>(
2525        &self,
2526        buffer_id: id::BufferId,
2527        offset: BufferAddress,
2528        size: Option<BufferAddress>,
2529    ) -> Result<(*mut u8, u64), BufferAccessError> {
2530        profiling::scope!("Buffer::get_mapped_range");
2531        api_log!("Buffer::get_mapped_range {buffer_id:?} offset {offset:?} size {size:?}");
2532
2533        let hub = A::hub(self);
2534
2535        let buffer = hub
2536            .buffers
2537            .get(buffer_id)
2538            .map_err(|_| BufferAccessError::Invalid)?;
2539
2540        {
2541            let snatch_guard = buffer.device.snatchable_lock.read();
2542            if buffer.is_destroyed(&snatch_guard) {
2543                return Err(BufferAccessError::Destroyed);
2544            }
2545        }
2546
2547        let range_size = if let Some(size) = size {
2548            size
2549        } else if offset > buffer.size {
2550            0
2551        } else {
2552            buffer.size - offset
2553        };
2554
2555        if offset % wgt::MAP_ALIGNMENT != 0 {
2556            return Err(BufferAccessError::UnalignedOffset { offset });
2557        }
2558        if range_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
2559            return Err(BufferAccessError::UnalignedRangeSize { range_size });
2560        }
2561        let map_state = &*buffer.map_state.lock();
2562        match *map_state {
2563            resource::BufferMapState::Init { ref ptr, .. } => {
2564                // offset (u64) can not be < 0, so no need to validate the lower bound
2565                if offset + range_size > buffer.size {
2566                    return Err(BufferAccessError::OutOfBoundsOverrun {
2567                        index: offset + range_size - 1,
2568                        max: buffer.size,
2569                    });
2570                }
2571                unsafe { Ok((ptr.as_ptr().offset(offset as isize), range_size)) }
2572            }
2573            resource::BufferMapState::Active {
2574                ref ptr, ref range, ..
2575            } => {
2576                if offset < range.start {
2577                    return Err(BufferAccessError::OutOfBoundsUnderrun {
2578                        index: offset,
2579                        min: range.start,
2580                    });
2581                }
2582                if offset + range_size > range.end {
2583                    return Err(BufferAccessError::OutOfBoundsOverrun {
2584                        index: offset + range_size - 1,
2585                        max: range.end,
2586                    });
2587                }
2588                // ptr points to the beginning of the range we mapped in map_async
2589                // rather than the beginning of the buffer.
2590                let relative_offset = (offset - range.start) as isize;
2591                unsafe { Ok((ptr.as_ptr().offset(relative_offset), range_size)) }
2592            }
2593            resource::BufferMapState::Idle | resource::BufferMapState::Waiting(_) => {
2594                Err(BufferAccessError::NotMapped)
2595            }
2596        }
2597    }
2598    pub fn buffer_unmap<A: HalApi>(&self, buffer_id: id::BufferId) -> BufferAccessResult {
2599        profiling::scope!("unmap", "Buffer");
2600        api_log!("Buffer::unmap {buffer_id:?}");
2601
2602        let hub = A::hub(self);
2603
2604        let buffer = hub
2605            .buffers
2606            .get(buffer_id)
2607            .map_err(|_| BufferAccessError::Invalid)?;
2608
2609        let snatch_guard = buffer.device.snatchable_lock.read();
2610        if buffer.is_destroyed(&snatch_guard) {
2611            return Err(BufferAccessError::Destroyed);
2612        }
2613        drop(snatch_guard);
2614
2615        if !buffer.device.is_valid() {
2616            return Err(DeviceError::Lost.into());
2617        }
2618
2619        buffer.unmap()
2620    }
2621}
2622
2623struct DevicePoll {
2624    closures: UserClosures,
2625    queue_empty: bool,
2626}