wgpu_core/device/
global.rs

1use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc, vec::Vec};
2use core::{ptr::NonNull, sync::atomic::Ordering};
3
4#[cfg(feature = "trace")]
5use crate::device::trace;
6use crate::{
7    api_log,
8    binding_model::{
9        self, BindGroupEntry, BindingResource, BufferBinding, ResolvedBindGroupDescriptor,
10        ResolvedBindGroupEntry, ResolvedBindingResource, ResolvedBufferBinding,
11    },
12    command::{self, CommandBuffer},
13    conv,
14    device::{bgl, life::WaitIdleError, DeviceError, DeviceLostClosure},
15    global::Global,
16    hal_api::HalApi,
17    id::{self, AdapterId, DeviceId, QueueId, SurfaceId},
18    instance::{self, Adapter, Surface},
19    pipeline::{
20        self, ResolvedComputePipelineDescriptor, ResolvedFragmentState,
21        ResolvedProgrammableStageDescriptor, ResolvedRenderPipelineDescriptor, ResolvedVertexState,
22    },
23    present,
24    resource::{
25        self, BufferAccessError, BufferAccessResult, BufferMapOperation, CreateBufferError,
26        Fallible,
27    },
28    storage::Storage,
29    Label, LabelHelpers,
30};
31
32use wgt::{BufferAddress, TextureFormat};
33
34use super::{ImplicitPipelineIds, UserClosures};
35
36impl Global {
37    pub fn adapter_is_surface_supported(
38        &self,
39        adapter_id: AdapterId,
40        surface_id: SurfaceId,
41    ) -> bool {
42        let surface = self.surfaces.get(surface_id);
43        let adapter = self.hub.adapters.get(adapter_id);
44        adapter.is_surface_supported(&surface)
45    }
46
47    pub fn surface_get_capabilities(
48        &self,
49        surface_id: SurfaceId,
50        adapter_id: AdapterId,
51    ) -> Result<wgt::SurfaceCapabilities, instance::GetSurfaceSupportError> {
52        profiling::scope!("Surface::get_capabilities");
53        self.fetch_adapter_and_surface::<_, _>(surface_id, adapter_id, |adapter, surface| {
54            let mut hal_caps = surface.get_capabilities(adapter)?;
55
56            hal_caps.formats.sort_by_key(|f| !f.is_srgb());
57
58            let usages = conv::map_texture_usage_from_hal(hal_caps.usage);
59
60            Ok(wgt::SurfaceCapabilities {
61                formats: hal_caps.formats,
62                present_modes: hal_caps.present_modes,
63                alpha_modes: hal_caps.composite_alpha_modes,
64                usages,
65            })
66        })
67    }
68
69    fn fetch_adapter_and_surface<F: FnOnce(&Adapter, &Surface) -> B, B>(
70        &self,
71        surface_id: SurfaceId,
72        adapter_id: AdapterId,
73        get_supported_callback: F,
74    ) -> B {
75        let surface = self.surfaces.get(surface_id);
76        let adapter = self.hub.adapters.get(adapter_id);
77        get_supported_callback(&adapter, &surface)
78    }
79
80    pub fn device_features(&self, device_id: DeviceId) -> wgt::Features {
81        let device = self.hub.devices.get(device_id);
82        device.features
83    }
84
85    pub fn device_limits(&self, device_id: DeviceId) -> wgt::Limits {
86        let device = self.hub.devices.get(device_id);
87        device.limits.clone()
88    }
89
90    pub fn device_downlevel_properties(&self, device_id: DeviceId) -> wgt::DownlevelCapabilities {
91        let device = self.hub.devices.get(device_id);
92        device.downlevel.clone()
93    }
94
95    pub fn device_create_buffer(
96        &self,
97        device_id: DeviceId,
98        desc: &resource::BufferDescriptor,
99        id_in: Option<id::BufferId>,
100    ) -> (id::BufferId, Option<CreateBufferError>) {
101        profiling::scope!("Device::create_buffer");
102
103        let hub = &self.hub;
104        let fid = hub.buffers.prepare(id_in);
105
106        let error = 'error: {
107            let device = self.hub.devices.get(device_id);
108
109            #[cfg(feature = "trace")]
110            if let Some(ref mut trace) = *device.trace.lock() {
111                let mut desc = desc.clone();
112                let mapped_at_creation = core::mem::replace(&mut desc.mapped_at_creation, false);
113                if mapped_at_creation && !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
114                    desc.usage |= wgt::BufferUsages::COPY_DST;
115                }
116                trace.add(trace::Action::CreateBuffer(fid.id(), desc));
117            }
118
119            let buffer = match device.create_buffer(desc) {
120                Ok(buffer) => buffer,
121                Err(e) => {
122                    break 'error e;
123                }
124            };
125
126            let id = fid.assign(Fallible::Valid(buffer));
127
128            api_log!(
129                "Device::create_buffer({:?}{}) -> {id:?}",
130                desc.label.as_deref().unwrap_or(""),
131                if desc.mapped_at_creation {
132                    ", mapped_at_creation"
133                } else {
134                    ""
135                }
136            );
137
138            return (id, None);
139        };
140
141        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
142        (id, Some(error))
143    }
144
145    /// Assign `id_in` an error with the given `label`.
146    ///
147    /// Ensure that future attempts to use `id_in` as a buffer ID will propagate
148    /// the error, following the WebGPU ["contagious invalidity"] style.
149    ///
150    /// Firefox uses this function to comply strictly with the WebGPU spec,
151    /// which requires [`GPUBufferDescriptor`] validation to be generated on the
152    /// Device timeline and leave the newly created [`GPUBuffer`] invalid.
153    ///
154    /// Ideally, we would simply let [`device_create_buffer`] take care of all
155    /// of this, but some errors must be detected before we can even construct a
156    /// [`wgpu_types::BufferDescriptor`] to give it. For example, the WebGPU API
157    /// allows a `GPUBufferDescriptor`'s [`usage`] property to be any WebIDL
158    /// `unsigned long` value, but we can't construct a
159    /// [`wgpu_types::BufferUsages`] value from values with unassigned bits
160    /// set. This means we must validate `usage` before we can call
161    /// `device_create_buffer`.
162    ///
163    /// When that validation fails, we must arrange for the buffer id to be
164    /// considered invalid. This method provides the means to do so.
165    ///
166    /// ["contagious invalidity"]: https://www.w3.org/TR/webgpu/#invalidity
167    /// [`GPUBufferDescriptor`]: https://www.w3.org/TR/webgpu/#dictdef-gpubufferdescriptor
168    /// [`GPUBuffer`]: https://www.w3.org/TR/webgpu/#gpubuffer
169    /// [`wgpu_types::BufferDescriptor`]: wgt::BufferDescriptor
170    /// [`device_create_buffer`]: Global::device_create_buffer
171    /// [`usage`]: https://www.w3.org/TR/webgpu/#dom-gputexturedescriptor-usage
172    /// [`wgpu_types::BufferUsages`]: wgt::BufferUsages
173    pub fn create_buffer_error(
174        &self,
175        id_in: Option<id::BufferId>,
176        desc: &resource::BufferDescriptor,
177    ) {
178        let fid = self.hub.buffers.prepare(id_in);
179        fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
180    }
181
182    pub fn create_render_bundle_error(
183        &self,
184        id_in: Option<id::RenderBundleId>,
185        desc: &command::RenderBundleDescriptor,
186    ) {
187        let fid = self.hub.render_bundles.prepare(id_in);
188        fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
189    }
190
191    /// Assign `id_in` an error with the given `label`.
192    ///
193    /// See `create_buffer_error` for more context and explanation.
194    pub fn create_texture_error(
195        &self,
196        id_in: Option<id::TextureId>,
197        desc: &resource::TextureDescriptor,
198    ) {
199        let fid = self.hub.textures.prepare(id_in);
200        fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
201    }
202
203    #[cfg(feature = "replay")]
204    pub fn device_set_buffer_data(
205        &self,
206        buffer_id: id::BufferId,
207        offset: BufferAddress,
208        data: &[u8],
209    ) -> BufferAccessResult {
210        let hub = &self.hub;
211
212        let buffer = hub.buffers.get(buffer_id).get()?;
213
214        let device = &buffer.device;
215
216        device.check_is_valid()?;
217        buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?;
218
219        let last_submission = device.get_queue().and_then(|queue| {
220            queue
221                .lock_life()
222                .get_buffer_latest_submission_index(&buffer)
223        });
224
225        if let Some(last_submission) = last_submission {
226            device.wait_for_submit(last_submission)?;
227        }
228
229        let snatch_guard = device.snatchable_lock.read();
230        let raw_buf = buffer.try_raw(&snatch_guard)?;
231
232        let mapping = unsafe {
233            device
234                .raw()
235                .map_buffer(raw_buf, offset..offset + data.len() as u64)
236        }
237        .map_err(|e| device.handle_hal_error(e))?;
238
239        unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()) };
240
241        if !mapping.is_coherent {
242            #[allow(clippy::single_range_in_vec_init)]
243            unsafe {
244                device
245                    .raw()
246                    .flush_mapped_ranges(raw_buf, &[offset..offset + data.len() as u64])
247            };
248        }
249
250        unsafe { device.raw().unmap_buffer(raw_buf) };
251
252        Ok(())
253    }
254
255    pub fn buffer_destroy(&self, buffer_id: id::BufferId) -> Result<(), resource::DestroyError> {
256        profiling::scope!("Buffer::destroy");
257        api_log!("Buffer::destroy {buffer_id:?}");
258
259        let hub = &self.hub;
260
261        let buffer = hub.buffers.get(buffer_id).get()?;
262
263        #[cfg(feature = "trace")]
264        if let Some(trace) = buffer.device.trace.lock().as_mut() {
265            trace.add(trace::Action::FreeBuffer(buffer_id));
266        }
267
268        let _ = buffer.unmap(
269            #[cfg(feature = "trace")]
270            buffer_id,
271        );
272
273        buffer.destroy()
274    }
275
276    pub fn buffer_drop(&self, buffer_id: id::BufferId) {
277        profiling::scope!("Buffer::drop");
278        api_log!("Buffer::drop {buffer_id:?}");
279
280        let hub = &self.hub;
281
282        let buffer = match hub.buffers.remove(buffer_id).get() {
283            Ok(buffer) => buffer,
284            Err(_) => {
285                return;
286            }
287        };
288
289        #[cfg(feature = "trace")]
290        if let Some(t) = buffer.device.trace.lock().as_mut() {
291            t.add(trace::Action::DestroyBuffer(buffer_id));
292        }
293
294        let _ = buffer.unmap(
295            #[cfg(feature = "trace")]
296            buffer_id,
297        );
298    }
299
300    pub fn device_create_texture(
301        &self,
302        device_id: DeviceId,
303        desc: &resource::TextureDescriptor,
304        id_in: Option<id::TextureId>,
305    ) -> (id::TextureId, Option<resource::CreateTextureError>) {
306        profiling::scope!("Device::create_texture");
307
308        let hub = &self.hub;
309
310        let fid = hub.textures.prepare(id_in);
311
312        let error = 'error: {
313            let device = self.hub.devices.get(device_id);
314
315            #[cfg(feature = "trace")]
316            if let Some(ref mut trace) = *device.trace.lock() {
317                trace.add(trace::Action::CreateTexture(fid.id(), desc.clone()));
318            }
319
320            let texture = match device.create_texture(desc) {
321                Ok(texture) => texture,
322                Err(error) => break 'error error,
323            };
324
325            let id = fid.assign(Fallible::Valid(texture));
326            api_log!("Device::create_texture({desc:?}) -> {id:?}");
327
328            return (id, None);
329        };
330
331        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
332        (id, Some(error))
333    }
334
335    /// # Safety
336    ///
337    /// - `hal_texture` must be created from `device_id` corresponding raw handle.
338    /// - `hal_texture` must be created respecting `desc`
339    /// - `hal_texture` must be initialized
340    pub unsafe fn create_texture_from_hal(
341        &self,
342        hal_texture: Box<dyn hal::DynTexture>,
343        device_id: DeviceId,
344        desc: &resource::TextureDescriptor,
345        id_in: Option<id::TextureId>,
346    ) -> (id::TextureId, Option<resource::CreateTextureError>) {
347        profiling::scope!("Device::create_texture_from_hal");
348
349        let hub = &self.hub;
350
351        let fid = hub.textures.prepare(id_in);
352
353        let error = 'error: {
354            let device = self.hub.devices.get(device_id);
355
356            // NB: Any change done through the raw texture handle will not be
357            // recorded in the replay
358            #[cfg(feature = "trace")]
359            if let Some(ref mut trace) = *device.trace.lock() {
360                trace.add(trace::Action::CreateTexture(fid.id(), desc.clone()));
361            }
362
363            let texture = match device.create_texture_from_hal(hal_texture, desc) {
364                Ok(texture) => texture,
365                Err(error) => break 'error error,
366            };
367
368            let id = fid.assign(Fallible::Valid(texture));
369            api_log!("Device::create_texture({desc:?}) -> {id:?}");
370
371            return (id, None);
372        };
373
374        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
375        (id, Some(error))
376    }
377
378    /// # Safety
379    ///
380    /// - `hal_buffer` must be created from `device_id` corresponding raw handle.
381    /// - `hal_buffer` must be created respecting `desc`
382    /// - `hal_buffer` must be initialized
383    pub unsafe fn create_buffer_from_hal<A: HalApi>(
384        &self,
385        hal_buffer: A::Buffer,
386        device_id: DeviceId,
387        desc: &resource::BufferDescriptor,
388        id_in: Option<id::BufferId>,
389    ) -> (id::BufferId, Option<CreateBufferError>) {
390        profiling::scope!("Device::create_buffer");
391
392        let hub = &self.hub;
393        let fid = hub.buffers.prepare(id_in);
394
395        let device = self.hub.devices.get(device_id);
396
397        // NB: Any change done through the raw buffer handle will not be
398        // recorded in the replay
399        #[cfg(feature = "trace")]
400        if let Some(trace) = device.trace.lock().as_mut() {
401            trace.add(trace::Action::CreateBuffer(fid.id(), desc.clone()));
402        }
403
404        let (buffer, err) = device.create_buffer_from_hal(Box::new(hal_buffer), desc);
405
406        let id = fid.assign(buffer);
407        api_log!("Device::create_buffer -> {id:?}");
408
409        (id, err)
410    }
411
412    pub fn texture_destroy(&self, texture_id: id::TextureId) -> Result<(), resource::DestroyError> {
413        profiling::scope!("Texture::destroy");
414        api_log!("Texture::destroy {texture_id:?}");
415
416        let hub = &self.hub;
417
418        let texture = hub.textures.get(texture_id).get()?;
419
420        #[cfg(feature = "trace")]
421        if let Some(trace) = texture.device.trace.lock().as_mut() {
422            trace.add(trace::Action::FreeTexture(texture_id));
423        }
424
425        texture.destroy()
426    }
427
428    pub fn texture_drop(&self, texture_id: id::TextureId) {
429        profiling::scope!("Texture::drop");
430        api_log!("Texture::drop {texture_id:?}");
431
432        let hub = &self.hub;
433
434        let _texture = hub.textures.remove(texture_id);
435        #[cfg(feature = "trace")]
436        if let Ok(texture) = _texture.get() {
437            if let Some(t) = texture.device.trace.lock().as_mut() {
438                t.add(trace::Action::DestroyTexture(texture_id));
439            }
440        }
441    }
442
443    pub fn texture_create_view(
444        &self,
445        texture_id: id::TextureId,
446        desc: &resource::TextureViewDescriptor,
447        id_in: Option<id::TextureViewId>,
448    ) -> (id::TextureViewId, Option<resource::CreateTextureViewError>) {
449        profiling::scope!("Texture::create_view");
450
451        let hub = &self.hub;
452
453        let fid = hub.texture_views.prepare(id_in);
454
455        let error = 'error: {
456            let texture = match hub.textures.get(texture_id).get() {
457                Ok(texture) => texture,
458                Err(e) => break 'error e.into(),
459            };
460            let device = &texture.device;
461
462            #[cfg(feature = "trace")]
463            if let Some(ref mut trace) = *device.trace.lock() {
464                trace.add(trace::Action::CreateTextureView {
465                    id: fid.id(),
466                    parent_id: texture_id,
467                    desc: desc.clone(),
468                });
469            }
470
471            let view = match device.create_texture_view(&texture, desc) {
472                Ok(view) => view,
473                Err(e) => break 'error e,
474            };
475
476            let id = fid.assign(Fallible::Valid(view));
477
478            api_log!("Texture::create_view({texture_id:?}) -> {id:?}");
479
480            return (id, None);
481        };
482
483        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
484        (id, Some(error))
485    }
486
487    pub fn texture_view_drop(
488        &self,
489        texture_view_id: id::TextureViewId,
490    ) -> Result<(), resource::TextureViewDestroyError> {
491        profiling::scope!("TextureView::drop");
492        api_log!("TextureView::drop {texture_view_id:?}");
493
494        let hub = &self.hub;
495
496        let _view = hub.texture_views.remove(texture_view_id);
497
498        #[cfg(feature = "trace")]
499        if let Ok(view) = _view.get() {
500            if let Some(t) = view.device.trace.lock().as_mut() {
501                t.add(trace::Action::DestroyTextureView(texture_view_id));
502            }
503        }
504        Ok(())
505    }
506
507    pub fn device_create_sampler(
508        &self,
509        device_id: DeviceId,
510        desc: &resource::SamplerDescriptor,
511        id_in: Option<id::SamplerId>,
512    ) -> (id::SamplerId, Option<resource::CreateSamplerError>) {
513        profiling::scope!("Device::create_sampler");
514
515        let hub = &self.hub;
516        let fid = hub.samplers.prepare(id_in);
517
518        let error = 'error: {
519            let device = self.hub.devices.get(device_id);
520
521            #[cfg(feature = "trace")]
522            if let Some(ref mut trace) = *device.trace.lock() {
523                trace.add(trace::Action::CreateSampler(fid.id(), desc.clone()));
524            }
525
526            let sampler = match device.create_sampler(desc) {
527                Ok(sampler) => sampler,
528                Err(e) => break 'error e,
529            };
530
531            let id = fid.assign(Fallible::Valid(sampler));
532            api_log!("Device::create_sampler -> {id:?}");
533
534            return (id, None);
535        };
536
537        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
538        (id, Some(error))
539    }
540
541    pub fn sampler_drop(&self, sampler_id: id::SamplerId) {
542        profiling::scope!("Sampler::drop");
543        api_log!("Sampler::drop {sampler_id:?}");
544
545        let hub = &self.hub;
546
547        let _sampler = hub.samplers.remove(sampler_id);
548
549        #[cfg(feature = "trace")]
550        if let Ok(sampler) = _sampler.get() {
551            if let Some(t) = sampler.device.trace.lock().as_mut() {
552                t.add(trace::Action::DestroySampler(sampler_id));
553            }
554        }
555    }
556
557    pub fn device_create_bind_group_layout(
558        &self,
559        device_id: DeviceId,
560        desc: &binding_model::BindGroupLayoutDescriptor,
561        id_in: Option<id::BindGroupLayoutId>,
562    ) -> (
563        id::BindGroupLayoutId,
564        Option<binding_model::CreateBindGroupLayoutError>,
565    ) {
566        profiling::scope!("Device::create_bind_group_layout");
567
568        let hub = &self.hub;
569        let fid = hub.bind_group_layouts.prepare(id_in);
570
571        let error = 'error: {
572            let device = self.hub.devices.get(device_id);
573
574            #[cfg(feature = "trace")]
575            if let Some(ref mut trace) = *device.trace.lock() {
576                trace.add(trace::Action::CreateBindGroupLayout(fid.id(), desc.clone()));
577            }
578
579            // this check can't go in the body of `create_bind_group_layout` since the closure might not get called
580            if let Err(e) = device.check_is_valid() {
581                break 'error e.into();
582            }
583
584            let entry_map = match bgl::EntryMap::from_entries(&device.limits, &desc.entries) {
585                Ok(map) => map,
586                Err(e) => break 'error e,
587            };
588
589            let bgl_result = device.bgl_pool.get_or_init(entry_map, |entry_map| {
590                let bgl =
591                    device.create_bind_group_layout(&desc.label, entry_map, bgl::Origin::Pool)?;
592                bgl.exclusive_pipeline
593                    .set(binding_model::ExclusivePipeline::None)
594                    .unwrap();
595                Ok(bgl)
596            });
597
598            let layout = match bgl_result {
599                Ok(layout) => layout,
600                Err(e) => break 'error e,
601            };
602
603            let id = fid.assign(Fallible::Valid(layout.clone()));
604
605            api_log!("Device::create_bind_group_layout -> {id:?}");
606            return (id, None);
607        };
608
609        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
610        (id, Some(error))
611    }
612
613    pub fn bind_group_layout_drop(&self, bind_group_layout_id: id::BindGroupLayoutId) {
614        profiling::scope!("BindGroupLayout::drop");
615        api_log!("BindGroupLayout::drop {bind_group_layout_id:?}");
616
617        let hub = &self.hub;
618
619        let _layout = hub.bind_group_layouts.remove(bind_group_layout_id);
620
621        #[cfg(feature = "trace")]
622        if let Ok(layout) = _layout.get() {
623            if let Some(t) = layout.device.trace.lock().as_mut() {
624                t.add(trace::Action::DestroyBindGroupLayout(bind_group_layout_id));
625            }
626        }
627    }
628
629    pub fn device_create_pipeline_layout(
630        &self,
631        device_id: DeviceId,
632        desc: &binding_model::PipelineLayoutDescriptor,
633        id_in: Option<id::PipelineLayoutId>,
634    ) -> (
635        id::PipelineLayoutId,
636        Option<binding_model::CreatePipelineLayoutError>,
637    ) {
638        profiling::scope!("Device::create_pipeline_layout");
639
640        let hub = &self.hub;
641        let fid = hub.pipeline_layouts.prepare(id_in);
642
643        let error = 'error: {
644            let device = self.hub.devices.get(device_id);
645
646            #[cfg(feature = "trace")]
647            if let Some(ref mut trace) = *device.trace.lock() {
648                trace.add(trace::Action::CreatePipelineLayout(fid.id(), desc.clone()));
649            }
650
651            let bind_group_layouts = {
652                let bind_group_layouts_guard = hub.bind_group_layouts.read();
653                desc.bind_group_layouts
654                    .iter()
655                    .map(|bgl_id| bind_group_layouts_guard.get(*bgl_id).get())
656                    .collect::<Result<Vec<_>, _>>()
657            };
658
659            let bind_group_layouts = match bind_group_layouts {
660                Ok(bind_group_layouts) => bind_group_layouts,
661                Err(e) => break 'error e.into(),
662            };
663
664            let desc = binding_model::ResolvedPipelineLayoutDescriptor {
665                label: desc.label.clone(),
666                bind_group_layouts: Cow::Owned(bind_group_layouts),
667                push_constant_ranges: desc.push_constant_ranges.clone(),
668            };
669
670            let layout = match device.create_pipeline_layout(&desc) {
671                Ok(layout) => layout,
672                Err(e) => break 'error e,
673            };
674
675            let id = fid.assign(Fallible::Valid(layout));
676            api_log!("Device::create_pipeline_layout -> {id:?}");
677            return (id, None);
678        };
679
680        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
681        (id, Some(error))
682    }
683
684    pub fn pipeline_layout_drop(&self, pipeline_layout_id: id::PipelineLayoutId) {
685        profiling::scope!("PipelineLayout::drop");
686        api_log!("PipelineLayout::drop {pipeline_layout_id:?}");
687
688        let hub = &self.hub;
689
690        let _layout = hub.pipeline_layouts.remove(pipeline_layout_id);
691
692        #[cfg(feature = "trace")]
693        if let Ok(layout) = _layout.get() {
694            if let Some(t) = layout.device.trace.lock().as_mut() {
695                t.add(trace::Action::DestroyPipelineLayout(pipeline_layout_id));
696            }
697        }
698    }
699
700    pub fn device_create_bind_group(
701        &self,
702        device_id: DeviceId,
703        desc: &binding_model::BindGroupDescriptor,
704        id_in: Option<id::BindGroupId>,
705    ) -> (id::BindGroupId, Option<binding_model::CreateBindGroupError>) {
706        profiling::scope!("Device::create_bind_group");
707
708        let hub = &self.hub;
709        let fid = hub.bind_groups.prepare(id_in);
710
711        let error = 'error: {
712            let device = self.hub.devices.get(device_id);
713
714            #[cfg(feature = "trace")]
715            if let Some(ref mut trace) = *device.trace.lock() {
716                trace.add(trace::Action::CreateBindGroup(fid.id(), desc.clone()));
717            }
718
719            let layout = match hub.bind_group_layouts.get(desc.layout).get() {
720                Ok(layout) => layout,
721                Err(e) => break 'error e.into(),
722            };
723
724            fn resolve_entry<'a>(
725                e: &BindGroupEntry<'a>,
726                buffer_storage: &Storage<Fallible<resource::Buffer>>,
727                sampler_storage: &Storage<Fallible<resource::Sampler>>,
728                texture_view_storage: &Storage<Fallible<resource::TextureView>>,
729                tlas_storage: &Storage<Fallible<resource::Tlas>>,
730            ) -> Result<ResolvedBindGroupEntry<'a>, binding_model::CreateBindGroupError>
731            {
732                let resolve_buffer = |bb: &BufferBinding| {
733                    buffer_storage
734                        .get(bb.buffer)
735                        .get()
736                        .map(|buffer| ResolvedBufferBinding {
737                            buffer,
738                            offset: bb.offset,
739                            size: bb.size,
740                        })
741                        .map_err(binding_model::CreateBindGroupError::from)
742                };
743                let resolve_sampler = |id: &id::SamplerId| {
744                    sampler_storage
745                        .get(*id)
746                        .get()
747                        .map_err(binding_model::CreateBindGroupError::from)
748                };
749                let resolve_view = |id: &id::TextureViewId| {
750                    texture_view_storage
751                        .get(*id)
752                        .get()
753                        .map_err(binding_model::CreateBindGroupError::from)
754                };
755                let resolve_tlas = |id: &id::TlasId| {
756                    tlas_storage
757                        .get(*id)
758                        .get()
759                        .map_err(binding_model::CreateBindGroupError::from)
760                };
761                let resource = match e.resource {
762                    BindingResource::Buffer(ref buffer) => {
763                        ResolvedBindingResource::Buffer(resolve_buffer(buffer)?)
764                    }
765                    BindingResource::BufferArray(ref buffers) => {
766                        let buffers = buffers
767                            .iter()
768                            .map(resolve_buffer)
769                            .collect::<Result<Vec<_>, _>>()?;
770                        ResolvedBindingResource::BufferArray(Cow::Owned(buffers))
771                    }
772                    BindingResource::Sampler(ref sampler) => {
773                        ResolvedBindingResource::Sampler(resolve_sampler(sampler)?)
774                    }
775                    BindingResource::SamplerArray(ref samplers) => {
776                        let samplers = samplers
777                            .iter()
778                            .map(resolve_sampler)
779                            .collect::<Result<Vec<_>, _>>()?;
780                        ResolvedBindingResource::SamplerArray(Cow::Owned(samplers))
781                    }
782                    BindingResource::TextureView(ref view) => {
783                        ResolvedBindingResource::TextureView(resolve_view(view)?)
784                    }
785                    BindingResource::TextureViewArray(ref views) => {
786                        let views = views
787                            .iter()
788                            .map(resolve_view)
789                            .collect::<Result<Vec<_>, _>>()?;
790                        ResolvedBindingResource::TextureViewArray(Cow::Owned(views))
791                    }
792                    BindingResource::AccelerationStructure(ref tlas) => {
793                        ResolvedBindingResource::AccelerationStructure(resolve_tlas(tlas)?)
794                    }
795                };
796                Ok(ResolvedBindGroupEntry {
797                    binding: e.binding,
798                    resource,
799                })
800            }
801
802            let entries = {
803                let buffer_guard = hub.buffers.read();
804                let texture_view_guard = hub.texture_views.read();
805                let sampler_guard = hub.samplers.read();
806                let tlas_guard = hub.tlas_s.read();
807                desc.entries
808                    .iter()
809                    .map(|e| {
810                        resolve_entry(
811                            e,
812                            &buffer_guard,
813                            &sampler_guard,
814                            &texture_view_guard,
815                            &tlas_guard,
816                        )
817                    })
818                    .collect::<Result<Vec<_>, _>>()
819            };
820            let entries = match entries {
821                Ok(entries) => Cow::Owned(entries),
822                Err(e) => break 'error e,
823            };
824
825            let desc = ResolvedBindGroupDescriptor {
826                label: desc.label.clone(),
827                layout,
828                entries,
829            };
830
831            let bind_group = match device.create_bind_group(desc) {
832                Ok(bind_group) => bind_group,
833                Err(e) => break 'error e,
834            };
835
836            let id = fid.assign(Fallible::Valid(bind_group));
837
838            api_log!("Device::create_bind_group -> {id:?}");
839
840            return (id, None);
841        };
842
843        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
844        (id, Some(error))
845    }
846
847    pub fn bind_group_drop(&self, bind_group_id: id::BindGroupId) {
848        profiling::scope!("BindGroup::drop");
849        api_log!("BindGroup::drop {bind_group_id:?}");
850
851        let hub = &self.hub;
852
853        let _bind_group = hub.bind_groups.remove(bind_group_id);
854
855        #[cfg(feature = "trace")]
856        if let Ok(_bind_group) = _bind_group.get() {
857            if let Some(t) = _bind_group.device.trace.lock().as_mut() {
858                t.add(trace::Action::DestroyBindGroup(bind_group_id));
859            }
860        }
861    }
862
863    /// Create a shader module with the given `source`.
864    ///
865    /// <div class="warning">
866    // NOTE: Keep this in sync with `naga::front::wgsl::parse_str`!
867    // NOTE: Keep this in sync with `wgpu::Device::create_shader_module`!
868    ///
869    /// This function may consume a lot of stack space. Compiler-enforced limits for parsing
870    /// recursion exist; if shader compilation runs into them, it will return an error gracefully.
871    /// However, on some build profiles and platforms, the default stack size for a thread may be
872    /// exceeded before this limit is reached during parsing. Callers should ensure that there is
873    /// enough stack space for this, particularly if calls to this method are exposed to user
874    /// input.
875    ///
876    /// </div>
877    pub fn device_create_shader_module(
878        &self,
879        device_id: DeviceId,
880        desc: &pipeline::ShaderModuleDescriptor,
881        source: pipeline::ShaderModuleSource,
882        id_in: Option<id::ShaderModuleId>,
883    ) -> (
884        id::ShaderModuleId,
885        Option<pipeline::CreateShaderModuleError>,
886    ) {
887        profiling::scope!("Device::create_shader_module");
888
889        let hub = &self.hub;
890        let fid = hub.shader_modules.prepare(id_in);
891
892        let error = 'error: {
893            let device = self.hub.devices.get(device_id);
894
895            #[cfg(feature = "trace")]
896            if let Some(ref mut trace) = *device.trace.lock() {
897                let data = match source {
898                    #[cfg(feature = "wgsl")]
899                    pipeline::ShaderModuleSource::Wgsl(ref code) => {
900                        trace.make_binary("wgsl", code.as_bytes())
901                    }
902                    #[cfg(feature = "glsl")]
903                    pipeline::ShaderModuleSource::Glsl(ref code, _) => {
904                        trace.make_binary("glsl", code.as_bytes())
905                    }
906                    #[cfg(feature = "spirv")]
907                    pipeline::ShaderModuleSource::SpirV(ref code, _) => {
908                        trace.make_binary("spirv", bytemuck::cast_slice::<u32, u8>(code))
909                    }
910                    pipeline::ShaderModuleSource::Naga(ref module) => {
911                        let string =
912                            ron::ser::to_string_pretty(module, ron::ser::PrettyConfig::default())
913                                .unwrap();
914                        trace.make_binary("ron", string.as_bytes())
915                    }
916                    pipeline::ShaderModuleSource::Dummy(_) => {
917                        panic!("found `ShaderModuleSource::Dummy`")
918                    }
919                };
920                trace.add(trace::Action::CreateShaderModule {
921                    id: fid.id(),
922                    desc: desc.clone(),
923                    data,
924                });
925            };
926
927            let shader = match device.create_shader_module(desc, source) {
928                Ok(shader) => shader,
929                Err(e) => break 'error e,
930            };
931
932            let id = fid.assign(Fallible::Valid(shader));
933            api_log!("Device::create_shader_module -> {id:?}");
934            return (id, None);
935        };
936
937        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
938        (id, Some(error))
939    }
940
941    #[allow(unused_unsafe)]
942    /// # Safety
943    ///
944    /// This function passes source code or binary to the backend as-is and can potentially result in a
945    /// driver crash.
946    pub unsafe fn device_create_shader_module_passthrough(
947        &self,
948        device_id: DeviceId,
949        desc: &pipeline::ShaderModuleDescriptorPassthrough<'_>,
950        id_in: Option<id::ShaderModuleId>,
951    ) -> (
952        id::ShaderModuleId,
953        Option<pipeline::CreateShaderModuleError>,
954    ) {
955        profiling::scope!("Device::create_shader_module_passthrough");
956
957        let hub = &self.hub;
958        let fid = hub.shader_modules.prepare(id_in);
959
960        let error = 'error: {
961            let device = self.hub.devices.get(device_id);
962
963            #[cfg(feature = "trace")]
964            if let Some(ref mut trace) = *device.trace.lock() {
965                let data = trace.make_binary(desc.trace_binary_ext(), desc.trace_data());
966                trace.add(trace::Action::CreateShaderModule {
967                    id: fid.id(),
968                    desc: match desc {
969                        pipeline::ShaderModuleDescriptorPassthrough::SpirV(inner) => {
970                            pipeline::ShaderModuleDescriptor {
971                                label: inner.label.clone(),
972                                runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),
973                            }
974                        }
975                        pipeline::ShaderModuleDescriptorPassthrough::Msl(inner) => {
976                            pipeline::ShaderModuleDescriptor {
977                                label: inner.label.clone(),
978                                runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),
979                            }
980                        }
981                    },
982                    data,
983                });
984            };
985
986            let result = unsafe { device.create_shader_module_passthrough(desc) };
987
988            let shader = match result {
989                Ok(shader) => shader,
990                Err(e) => break 'error e,
991            };
992            let id = fid.assign(Fallible::Valid(shader));
993            api_log!("Device::create_shader_module_spirv -> {id:?}");
994            return (id, None);
995        };
996
997        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label().to_string())));
998        (id, Some(error))
999    }
1000
1001    pub fn shader_module_drop(&self, shader_module_id: id::ShaderModuleId) {
1002        profiling::scope!("ShaderModule::drop");
1003        api_log!("ShaderModule::drop {shader_module_id:?}");
1004
1005        let hub = &self.hub;
1006
1007        let _shader_module = hub.shader_modules.remove(shader_module_id);
1008
1009        #[cfg(feature = "trace")]
1010        if let Ok(shader_module) = _shader_module.get() {
1011            if let Some(t) = shader_module.device.trace.lock().as_mut() {
1012                t.add(trace::Action::DestroyShaderModule(shader_module_id));
1013            }
1014        }
1015    }
1016
1017    pub fn device_create_command_encoder(
1018        &self,
1019        device_id: DeviceId,
1020        desc: &wgt::CommandEncoderDescriptor<Label>,
1021        id_in: Option<id::CommandEncoderId>,
1022    ) -> (id::CommandEncoderId, Option<DeviceError>) {
1023        profiling::scope!("Device::create_command_encoder");
1024
1025        let hub = &self.hub;
1026        let fid = hub
1027            .command_buffers
1028            .prepare(id_in.map(|id| id.into_command_buffer_id()));
1029
1030        let device = self.hub.devices.get(device_id);
1031
1032        let error = 'error: {
1033            let command_buffer = match device.create_command_encoder(&desc.label) {
1034                Ok(command_buffer) => command_buffer,
1035                Err(e) => break 'error e,
1036            };
1037
1038            let id = fid.assign(command_buffer);
1039            api_log!("Device::create_command_encoder -> {id:?}");
1040            return (id.into_command_encoder_id(), None);
1041        };
1042
1043        let id = fid.assign(Arc::new(CommandBuffer::new_invalid(&device, &desc.label)));
1044        (id.into_command_encoder_id(), Some(error))
1045    }
1046
1047    pub fn command_encoder_drop(&self, command_encoder_id: id::CommandEncoderId) {
1048        profiling::scope!("CommandEncoder::drop");
1049        api_log!("CommandEncoder::drop {command_encoder_id:?}");
1050
1051        let hub = &self.hub;
1052
1053        let _cmd_buf = hub
1054            .command_buffers
1055            .remove(command_encoder_id.into_command_buffer_id());
1056    }
1057
1058    pub fn command_buffer_drop(&self, command_buffer_id: id::CommandBufferId) {
1059        profiling::scope!("CommandBuffer::drop");
1060        api_log!("CommandBuffer::drop {command_buffer_id:?}");
1061        self.command_encoder_drop(command_buffer_id.into_command_encoder_id())
1062    }
1063
1064    pub fn device_create_render_bundle_encoder(
1065        &self,
1066        device_id: DeviceId,
1067        desc: &command::RenderBundleEncoderDescriptor,
1068    ) -> (
1069        *mut command::RenderBundleEncoder,
1070        Option<command::CreateRenderBundleError>,
1071    ) {
1072        profiling::scope!("Device::create_render_bundle_encoder");
1073        api_log!("Device::device_create_render_bundle_encoder");
1074        let (encoder, error) = match command::RenderBundleEncoder::new(desc, device_id, None) {
1075            Ok(encoder) => (encoder, None),
1076            Err(e) => (command::RenderBundleEncoder::dummy(device_id), Some(e)),
1077        };
1078        (Box::into_raw(Box::new(encoder)), error)
1079    }
1080
1081    pub fn render_bundle_encoder_finish(
1082        &self,
1083        bundle_encoder: command::RenderBundleEncoder,
1084        desc: &command::RenderBundleDescriptor,
1085        id_in: Option<id::RenderBundleId>,
1086    ) -> (id::RenderBundleId, Option<command::RenderBundleError>) {
1087        profiling::scope!("RenderBundleEncoder::finish");
1088
1089        let hub = &self.hub;
1090
1091        let fid = hub.render_bundles.prepare(id_in);
1092
1093        let error = 'error: {
1094            let device = self.hub.devices.get(bundle_encoder.parent());
1095
1096            #[cfg(feature = "trace")]
1097            if let Some(ref mut trace) = *device.trace.lock() {
1098                trace.add(trace::Action::CreateRenderBundle {
1099                    id: fid.id(),
1100                    desc: trace::new_render_bundle_encoder_descriptor(
1101                        desc.label.clone(),
1102                        &bundle_encoder.context,
1103                        bundle_encoder.is_depth_read_only,
1104                        bundle_encoder.is_stencil_read_only,
1105                    ),
1106                    base: bundle_encoder.to_base_pass(),
1107                });
1108            }
1109
1110            let render_bundle = match bundle_encoder.finish(desc, &device, hub) {
1111                Ok(bundle) => bundle,
1112                Err(e) => break 'error e,
1113            };
1114
1115            let id = fid.assign(Fallible::Valid(render_bundle));
1116            api_log!("RenderBundleEncoder::finish -> {id:?}");
1117
1118            return (id, None);
1119        };
1120
1121        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
1122        (id, Some(error))
1123    }
1124
1125    pub fn render_bundle_drop(&self, render_bundle_id: id::RenderBundleId) {
1126        profiling::scope!("RenderBundle::drop");
1127        api_log!("RenderBundle::drop {render_bundle_id:?}");
1128
1129        let hub = &self.hub;
1130
1131        let _bundle = hub.render_bundles.remove(render_bundle_id);
1132
1133        #[cfg(feature = "trace")]
1134        if let Ok(bundle) = _bundle.get() {
1135            if let Some(t) = bundle.device.trace.lock().as_mut() {
1136                t.add(trace::Action::DestroyRenderBundle(render_bundle_id));
1137            }
1138        }
1139    }
1140
1141    pub fn device_create_query_set(
1142        &self,
1143        device_id: DeviceId,
1144        desc: &resource::QuerySetDescriptor,
1145        id_in: Option<id::QuerySetId>,
1146    ) -> (id::QuerySetId, Option<resource::CreateQuerySetError>) {
1147        profiling::scope!("Device::create_query_set");
1148
1149        let hub = &self.hub;
1150        let fid = hub.query_sets.prepare(id_in);
1151
1152        let error = 'error: {
1153            let device = self.hub.devices.get(device_id);
1154
1155            #[cfg(feature = "trace")]
1156            if let Some(ref mut trace) = *device.trace.lock() {
1157                trace.add(trace::Action::CreateQuerySet {
1158                    id: fid.id(),
1159                    desc: desc.clone(),
1160                });
1161            }
1162
1163            let query_set = match device.create_query_set(desc) {
1164                Ok(query_set) => query_set,
1165                Err(err) => break 'error err,
1166            };
1167
1168            let id = fid.assign(Fallible::Valid(query_set));
1169            api_log!("Device::create_query_set -> {id:?}");
1170
1171            return (id, None);
1172        };
1173
1174        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
1175        (id, Some(error))
1176    }
1177
1178    pub fn query_set_drop(&self, query_set_id: id::QuerySetId) {
1179        profiling::scope!("QuerySet::drop");
1180        api_log!("QuerySet::drop {query_set_id:?}");
1181
1182        let hub = &self.hub;
1183
1184        let _query_set = hub.query_sets.remove(query_set_id);
1185
1186        #[cfg(feature = "trace")]
1187        if let Ok(query_set) = _query_set.get() {
1188            if let Some(trace) = query_set.device.trace.lock().as_mut() {
1189                trace.add(trace::Action::DestroyQuerySet(query_set_id));
1190            }
1191        }
1192    }
1193
1194    pub fn device_create_render_pipeline(
1195        &self,
1196        device_id: DeviceId,
1197        desc: &pipeline::RenderPipelineDescriptor,
1198        id_in: Option<id::RenderPipelineId>,
1199        implicit_pipeline_ids: Option<ImplicitPipelineIds<'_>>,
1200    ) -> (
1201        id::RenderPipelineId,
1202        Option<pipeline::CreateRenderPipelineError>,
1203    ) {
1204        profiling::scope!("Device::create_render_pipeline");
1205
1206        let hub = &self.hub;
1207
1208        let missing_implicit_pipeline_ids =
1209            desc.layout.is_none() && id_in.is_some() && implicit_pipeline_ids.is_none();
1210
1211        let fid = hub.render_pipelines.prepare(id_in);
1212        let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(hub));
1213
1214        let error = 'error: {
1215            if missing_implicit_pipeline_ids {
1216                // TODO: categorize this error as API misuse
1217                break 'error pipeline::ImplicitLayoutError::MissingImplicitPipelineIds.into();
1218            }
1219
1220            let device = self.hub.devices.get(device_id);
1221
1222            #[cfg(feature = "trace")]
1223            if let Some(ref mut trace) = *device.trace.lock() {
1224                trace.add(trace::Action::CreateRenderPipeline {
1225                    id: fid.id(),
1226                    desc: desc.clone(),
1227                    implicit_context: implicit_context.clone(),
1228                });
1229            }
1230
1231            let layout = desc
1232                .layout
1233                .map(|layout| hub.pipeline_layouts.get(layout).get())
1234                .transpose();
1235            let layout = match layout {
1236                Ok(layout) => layout,
1237                Err(e) => break 'error e.into(),
1238            };
1239
1240            let cache = desc
1241                .cache
1242                .map(|cache| hub.pipeline_caches.get(cache).get())
1243                .transpose();
1244            let cache = match cache {
1245                Ok(cache) => cache,
1246                Err(e) => break 'error e.into(),
1247            };
1248
1249            let vertex = {
1250                let module = hub
1251                    .shader_modules
1252                    .get(desc.vertex.stage.module)
1253                    .get()
1254                    .map_err(|e| pipeline::CreateRenderPipelineError::Stage {
1255                        stage: wgt::ShaderStages::VERTEX,
1256                        error: e.into(),
1257                    });
1258                let module = match module {
1259                    Ok(module) => module,
1260                    Err(e) => break 'error e,
1261                };
1262                let stage = ResolvedProgrammableStageDescriptor {
1263                    module,
1264                    entry_point: desc.vertex.stage.entry_point.clone(),
1265                    constants: desc.vertex.stage.constants.clone(),
1266                    zero_initialize_workgroup_memory: desc
1267                        .vertex
1268                        .stage
1269                        .zero_initialize_workgroup_memory,
1270                };
1271                ResolvedVertexState {
1272                    stage,
1273                    buffers: desc.vertex.buffers.clone(),
1274                }
1275            };
1276
1277            let fragment = if let Some(ref state) = desc.fragment {
1278                let module = hub
1279                    .shader_modules
1280                    .get(state.stage.module)
1281                    .get()
1282                    .map_err(|e| pipeline::CreateRenderPipelineError::Stage {
1283                        stage: wgt::ShaderStages::FRAGMENT,
1284                        error: e.into(),
1285                    });
1286                let module = match module {
1287                    Ok(module) => module,
1288                    Err(e) => break 'error e,
1289                };
1290                let stage = ResolvedProgrammableStageDescriptor {
1291                    module,
1292                    entry_point: state.stage.entry_point.clone(),
1293                    constants: state.stage.constants.clone(),
1294                    zero_initialize_workgroup_memory: desc
1295                        .vertex
1296                        .stage
1297                        .zero_initialize_workgroup_memory,
1298                };
1299                Some(ResolvedFragmentState {
1300                    stage,
1301                    targets: state.targets.clone(),
1302                })
1303            } else {
1304                None
1305            };
1306
1307            let desc = ResolvedRenderPipelineDescriptor {
1308                label: desc.label.clone(),
1309                layout,
1310                vertex,
1311                primitive: desc.primitive,
1312                depth_stencil: desc.depth_stencil.clone(),
1313                multisample: desc.multisample,
1314                fragment,
1315                multiview: desc.multiview,
1316                cache,
1317            };
1318
1319            let pipeline = match device.create_render_pipeline(desc) {
1320                Ok(pair) => pair,
1321                Err(e) => break 'error e,
1322            };
1323
1324            if let Some(ids) = implicit_context.as_ref() {
1325                let group_count = pipeline.layout.bind_group_layouts.len();
1326                if ids.group_ids.len() < group_count {
1327                    log::error!(
1328                        "Not enough bind group IDs ({}) specified for the implicit layout ({})",
1329                        ids.group_ids.len(),
1330                        group_count
1331                    );
1332                    // TODO: categorize this error as API misuse
1333                    break 'error pipeline::ImplicitLayoutError::MissingIds(group_count as _)
1334                        .into();
1335                }
1336
1337                let mut pipeline_layout_guard = hub.pipeline_layouts.write();
1338                let mut bgl_guard = hub.bind_group_layouts.write();
1339                pipeline_layout_guard.insert(ids.root_id, Fallible::Valid(pipeline.layout.clone()));
1340                let mut group_ids = ids.group_ids.iter();
1341                // NOTE: If the first iterator is longer than the second, the `.zip()` impl will still advance the
1342                // the first iterator before realizing that the second iterator has finished.
1343                // The `pipeline.layout.bind_group_layouts` iterator will always be shorter than `ids.group_ids`,
1344                // so using it as the first iterator for `.zip()` will work properly.
1345                for (bgl, bgl_id) in pipeline
1346                    .layout
1347                    .bind_group_layouts
1348                    .iter()
1349                    .zip(&mut group_ids)
1350                {
1351                    bgl_guard.insert(*bgl_id, Fallible::Valid(bgl.clone()));
1352                }
1353                for bgl_id in group_ids {
1354                    bgl_guard.insert(*bgl_id, Fallible::Invalid(Arc::new(String::new())));
1355                }
1356            }
1357
1358            let id = fid.assign(Fallible::Valid(pipeline));
1359            api_log!("Device::create_render_pipeline -> {id:?}");
1360
1361            return (id, None);
1362        };
1363
1364        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
1365
1366        // We also need to assign errors to the implicit pipeline layout and the
1367        // implicit bind group layouts.
1368        if let Some(ids) = implicit_context {
1369            let mut pipeline_layout_guard = hub.pipeline_layouts.write();
1370            let mut bgl_guard = hub.bind_group_layouts.write();
1371            pipeline_layout_guard.insert(ids.root_id, Fallible::Invalid(Arc::new(String::new())));
1372            for bgl_id in ids.group_ids {
1373                bgl_guard.insert(bgl_id, Fallible::Invalid(Arc::new(String::new())));
1374            }
1375        }
1376
1377        (id, Some(error))
1378    }
1379
1380    /// Get an ID of one of the bind group layouts. The ID adds a refcount,
1381    /// which needs to be released by calling `bind_group_layout_drop`.
1382    pub fn render_pipeline_get_bind_group_layout(
1383        &self,
1384        pipeline_id: id::RenderPipelineId,
1385        index: u32,
1386        id_in: Option<id::BindGroupLayoutId>,
1387    ) -> (
1388        id::BindGroupLayoutId,
1389        Option<binding_model::GetBindGroupLayoutError>,
1390    ) {
1391        let hub = &self.hub;
1392
1393        let fid = hub.bind_group_layouts.prepare(id_in);
1394
1395        let error = 'error: {
1396            let pipeline = match hub.render_pipelines.get(pipeline_id).get() {
1397                Ok(pipeline) => pipeline,
1398                Err(e) => break 'error e.into(),
1399            };
1400            let id = match pipeline.layout.bind_group_layouts.get(index as usize) {
1401                Some(bg) => fid.assign(Fallible::Valid(bg.clone())),
1402                None => {
1403                    break 'error binding_model::GetBindGroupLayoutError::InvalidGroupIndex(index)
1404                }
1405            };
1406            return (id, None);
1407        };
1408
1409        let id = fid.assign(Fallible::Invalid(Arc::new(String::new())));
1410        (id, Some(error))
1411    }
1412
1413    pub fn render_pipeline_drop(&self, render_pipeline_id: id::RenderPipelineId) {
1414        profiling::scope!("RenderPipeline::drop");
1415        api_log!("RenderPipeline::drop {render_pipeline_id:?}");
1416
1417        let hub = &self.hub;
1418
1419        let _pipeline = hub.render_pipelines.remove(render_pipeline_id);
1420
1421        #[cfg(feature = "trace")]
1422        if let Ok(pipeline) = _pipeline.get() {
1423            if let Some(t) = pipeline.device.trace.lock().as_mut() {
1424                t.add(trace::Action::DestroyRenderPipeline(render_pipeline_id));
1425            }
1426        }
1427    }
1428
1429    pub fn device_create_compute_pipeline(
1430        &self,
1431        device_id: DeviceId,
1432        desc: &pipeline::ComputePipelineDescriptor,
1433        id_in: Option<id::ComputePipelineId>,
1434        implicit_pipeline_ids: Option<ImplicitPipelineIds<'_>>,
1435    ) -> (
1436        id::ComputePipelineId,
1437        Option<pipeline::CreateComputePipelineError>,
1438    ) {
1439        profiling::scope!("Device::create_compute_pipeline");
1440
1441        let hub = &self.hub;
1442
1443        let missing_implicit_pipeline_ids =
1444            desc.layout.is_none() && id_in.is_some() && implicit_pipeline_ids.is_none();
1445
1446        let fid = hub.compute_pipelines.prepare(id_in);
1447        let implicit_context = implicit_pipeline_ids.map(|ipi| ipi.prepare(hub));
1448
1449        let error = 'error: {
1450            if missing_implicit_pipeline_ids {
1451                // TODO: categorize this error as API misuse
1452                break 'error pipeline::ImplicitLayoutError::MissingImplicitPipelineIds.into();
1453            }
1454
1455            let device = self.hub.devices.get(device_id);
1456
1457            #[cfg(feature = "trace")]
1458            if let Some(ref mut trace) = *device.trace.lock() {
1459                trace.add(trace::Action::CreateComputePipeline {
1460                    id: fid.id(),
1461                    desc: desc.clone(),
1462                    implicit_context: implicit_context.clone(),
1463                });
1464            }
1465
1466            let layout = desc
1467                .layout
1468                .map(|layout| hub.pipeline_layouts.get(layout).get())
1469                .transpose();
1470            let layout = match layout {
1471                Ok(layout) => layout,
1472                Err(e) => break 'error e.into(),
1473            };
1474
1475            let cache = desc
1476                .cache
1477                .map(|cache| hub.pipeline_caches.get(cache).get())
1478                .transpose();
1479            let cache = match cache {
1480                Ok(cache) => cache,
1481                Err(e) => break 'error e.into(),
1482            };
1483
1484            let module = hub.shader_modules.get(desc.stage.module).get();
1485            let module = match module {
1486                Ok(module) => module,
1487                Err(e) => break 'error e.into(),
1488            };
1489            let stage = ResolvedProgrammableStageDescriptor {
1490                module,
1491                entry_point: desc.stage.entry_point.clone(),
1492                constants: desc.stage.constants.clone(),
1493                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
1494            };
1495
1496            let desc = ResolvedComputePipelineDescriptor {
1497                label: desc.label.clone(),
1498                layout,
1499                stage,
1500                cache,
1501            };
1502
1503            let pipeline = match device.create_compute_pipeline(desc) {
1504                Ok(pair) => pair,
1505                Err(e) => break 'error e,
1506            };
1507
1508            if let Some(ids) = implicit_context.as_ref() {
1509                let group_count = pipeline.layout.bind_group_layouts.len();
1510                if ids.group_ids.len() < group_count {
1511                    log::error!(
1512                        "Not enough bind group IDs ({}) specified for the implicit layout ({})",
1513                        ids.group_ids.len(),
1514                        group_count
1515                    );
1516                    // TODO: categorize this error as API misuse
1517                    break 'error pipeline::ImplicitLayoutError::MissingIds(group_count as _)
1518                        .into();
1519                }
1520
1521                let mut pipeline_layout_guard = hub.pipeline_layouts.write();
1522                let mut bgl_guard = hub.bind_group_layouts.write();
1523                pipeline_layout_guard.insert(ids.root_id, Fallible::Valid(pipeline.layout.clone()));
1524                let mut group_ids = ids.group_ids.iter();
1525                // NOTE: If the first iterator is longer than the second, the `.zip()` impl will still advance the
1526                // the first iterator before realizing that the second iterator has finished.
1527                // The `pipeline.layout.bind_group_layouts` iterator will always be shorter than `ids.group_ids`,
1528                // so using it as the first iterator for `.zip()` will work properly.
1529                for (bgl, bgl_id) in pipeline
1530                    .layout
1531                    .bind_group_layouts
1532                    .iter()
1533                    .zip(&mut group_ids)
1534                {
1535                    bgl_guard.insert(*bgl_id, Fallible::Valid(bgl.clone()));
1536                }
1537                for bgl_id in group_ids {
1538                    bgl_guard.insert(*bgl_id, Fallible::Invalid(Arc::new(String::new())));
1539                }
1540            }
1541
1542            let id = fid.assign(Fallible::Valid(pipeline));
1543            api_log!("Device::create_compute_pipeline -> {id:?}");
1544
1545            return (id, None);
1546        };
1547
1548        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
1549
1550        // We also need to assign errors to the implicit pipeline layout and the
1551        // implicit bind group layouts.
1552        if let Some(ids) = implicit_context {
1553            let mut pipeline_layout_guard = hub.pipeline_layouts.write();
1554            let mut bgl_guard = hub.bind_group_layouts.write();
1555            pipeline_layout_guard.insert(ids.root_id, Fallible::Invalid(Arc::new(String::new())));
1556            for bgl_id in ids.group_ids {
1557                bgl_guard.insert(bgl_id, Fallible::Invalid(Arc::new(String::new())));
1558            }
1559        }
1560
1561        (id, Some(error))
1562    }
1563
1564    /// Get an ID of one of the bind group layouts. The ID adds a refcount,
1565    /// which needs to be released by calling `bind_group_layout_drop`.
1566    pub fn compute_pipeline_get_bind_group_layout(
1567        &self,
1568        pipeline_id: id::ComputePipelineId,
1569        index: u32,
1570        id_in: Option<id::BindGroupLayoutId>,
1571    ) -> (
1572        id::BindGroupLayoutId,
1573        Option<binding_model::GetBindGroupLayoutError>,
1574    ) {
1575        let hub = &self.hub;
1576
1577        let fid = hub.bind_group_layouts.prepare(id_in);
1578
1579        let error = 'error: {
1580            let pipeline = match hub.compute_pipelines.get(pipeline_id).get() {
1581                Ok(pipeline) => pipeline,
1582                Err(e) => break 'error e.into(),
1583            };
1584
1585            let id = match pipeline.layout.bind_group_layouts.get(index as usize) {
1586                Some(bg) => fid.assign(Fallible::Valid(bg.clone())),
1587                None => {
1588                    break 'error binding_model::GetBindGroupLayoutError::InvalidGroupIndex(index)
1589                }
1590            };
1591
1592            return (id, None);
1593        };
1594
1595        let id = fid.assign(Fallible::Invalid(Arc::new(String::new())));
1596        (id, Some(error))
1597    }
1598
1599    pub fn compute_pipeline_drop(&self, compute_pipeline_id: id::ComputePipelineId) {
1600        profiling::scope!("ComputePipeline::drop");
1601        api_log!("ComputePipeline::drop {compute_pipeline_id:?}");
1602
1603        let hub = &self.hub;
1604
1605        let _pipeline = hub.compute_pipelines.remove(compute_pipeline_id);
1606
1607        #[cfg(feature = "trace")]
1608        if let Ok(pipeline) = _pipeline.get() {
1609            if let Some(t) = pipeline.device.trace.lock().as_mut() {
1610                t.add(trace::Action::DestroyComputePipeline(compute_pipeline_id));
1611            }
1612        }
1613    }
1614
1615    /// # Safety
1616    /// The `data` argument of `desc` must have been returned by
1617    /// [Self::pipeline_cache_get_data] for the same adapter
1618    pub unsafe fn device_create_pipeline_cache(
1619        &self,
1620        device_id: DeviceId,
1621        desc: &pipeline::PipelineCacheDescriptor<'_>,
1622        id_in: Option<id::PipelineCacheId>,
1623    ) -> (
1624        id::PipelineCacheId,
1625        Option<pipeline::CreatePipelineCacheError>,
1626    ) {
1627        profiling::scope!("Device::create_pipeline_cache");
1628
1629        let hub = &self.hub;
1630
1631        let fid = hub.pipeline_caches.prepare(id_in);
1632        let error: pipeline::CreatePipelineCacheError = 'error: {
1633            let device = self.hub.devices.get(device_id);
1634
1635            #[cfg(feature = "trace")]
1636            if let Some(ref mut trace) = *device.trace.lock() {
1637                trace.add(trace::Action::CreatePipelineCache {
1638                    id: fid.id(),
1639                    desc: desc.clone(),
1640                });
1641            }
1642
1643            let cache = unsafe { device.create_pipeline_cache(desc) };
1644            match cache {
1645                Ok(cache) => {
1646                    let id = fid.assign(Fallible::Valid(cache));
1647                    api_log!("Device::create_pipeline_cache -> {id:?}");
1648                    return (id, None);
1649                }
1650                Err(e) => break 'error e,
1651            }
1652        };
1653
1654        let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string())));
1655
1656        (id, Some(error))
1657    }
1658
1659    pub fn pipeline_cache_drop(&self, pipeline_cache_id: id::PipelineCacheId) {
1660        profiling::scope!("PipelineCache::drop");
1661        api_log!("PipelineCache::drop {pipeline_cache_id:?}");
1662
1663        let hub = &self.hub;
1664
1665        let _cache = hub.pipeline_caches.remove(pipeline_cache_id);
1666
1667        #[cfg(feature = "trace")]
1668        if let Ok(cache) = _cache.get() {
1669            if let Some(t) = cache.device.trace.lock().as_mut() {
1670                t.add(trace::Action::DestroyPipelineCache(pipeline_cache_id));
1671            }
1672        }
1673    }
1674
1675    pub fn surface_configure(
1676        &self,
1677        surface_id: SurfaceId,
1678        device_id: DeviceId,
1679        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
1680    ) -> Option<present::ConfigureSurfaceError> {
1681        use present::ConfigureSurfaceError as E;
1682        profiling::scope!("surface_configure");
1683
1684        fn validate_surface_configuration(
1685            config: &mut hal::SurfaceConfiguration,
1686            caps: &hal::SurfaceCapabilities,
1687            max_texture_dimension_2d: u32,
1688        ) -> Result<(), E> {
1689            let width = config.extent.width;
1690            let height = config.extent.height;
1691
1692            if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
1693                return Err(E::TooLarge {
1694                    width,
1695                    height,
1696                    max_texture_dimension_2d,
1697                });
1698            }
1699
1700            if !caps.present_modes.contains(&config.present_mode) {
1701                // Automatic present mode checks.
1702                //
1703                // The "Automatic" modes are never supported by the backends.
1704                let fallbacks = match config.present_mode {
1705                    wgt::PresentMode::AutoVsync => {
1706                        &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]
1707                    }
1708                    // Always end in FIFO to make sure it's always supported
1709                    wgt::PresentMode::AutoNoVsync => &[
1710                        wgt::PresentMode::Immediate,
1711                        wgt::PresentMode::Mailbox,
1712                        wgt::PresentMode::Fifo,
1713                    ][..],
1714                    _ => {
1715                        return Err(E::UnsupportedPresentMode {
1716                            requested: config.present_mode,
1717                            available: caps.present_modes.clone(),
1718                        });
1719                    }
1720                };
1721
1722                let new_mode = fallbacks
1723                    .iter()
1724                    .copied()
1725                    .find(|fallback| caps.present_modes.contains(fallback))
1726                    .unwrap_or_else(|| {
1727                        unreachable!(
1728                            "Fallback system failed to choose present mode. \
1729                            This is a bug. Mode: {:?}, Options: {:?}",
1730                            config.present_mode, &caps.present_modes
1731                        );
1732                    });
1733
1734                api_log!(
1735                    "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
1736                    config.present_mode
1737                );
1738                config.present_mode = new_mode;
1739            }
1740            if !caps.formats.contains(&config.format) {
1741                return Err(E::UnsupportedFormat {
1742                    requested: config.format,
1743                    available: caps.formats.clone(),
1744                });
1745            }
1746            if !caps
1747                .composite_alpha_modes
1748                .contains(&config.composite_alpha_mode)
1749            {
1750                let new_alpha_mode = 'alpha: {
1751                    // Automatic alpha mode checks.
1752                    let fallbacks = match config.composite_alpha_mode {
1753                        wgt::CompositeAlphaMode::Auto => &[
1754                            wgt::CompositeAlphaMode::Opaque,
1755                            wgt::CompositeAlphaMode::Inherit,
1756                        ][..],
1757                        _ => {
1758                            return Err(E::UnsupportedAlphaMode {
1759                                requested: config.composite_alpha_mode,
1760                                available: caps.composite_alpha_modes.clone(),
1761                            });
1762                        }
1763                    };
1764
1765                    for &fallback in fallbacks {
1766                        if caps.composite_alpha_modes.contains(&fallback) {
1767                            break 'alpha fallback;
1768                        }
1769                    }
1770
1771                    unreachable!(
1772                        "Fallback system failed to choose alpha mode. This is a bug. \
1773                                  AlphaMode: {:?}, Options: {:?}",
1774                        config.composite_alpha_mode, &caps.composite_alpha_modes
1775                    );
1776                };
1777
1778                api_log!(
1779                    "Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
1780                    config.composite_alpha_mode
1781                );
1782                config.composite_alpha_mode = new_alpha_mode;
1783            }
1784            if !caps.usage.contains(config.usage) {
1785                return Err(E::UnsupportedUsage {
1786                    requested: config.usage,
1787                    available: caps.usage,
1788                });
1789            }
1790            if width == 0 || height == 0 {
1791                return Err(E::ZeroArea);
1792            }
1793            Ok(())
1794        }
1795
1796        log::debug!("configuring surface with {:?}", config);
1797
1798        let error = 'error: {
1799            // User callbacks must not be called while we are holding locks.
1800            let user_callbacks;
1801            {
1802                let device = self.hub.devices.get(device_id);
1803
1804                #[cfg(feature = "trace")]
1805                if let Some(ref mut trace) = *device.trace.lock() {
1806                    trace.add(trace::Action::ConfigureSurface(surface_id, config.clone()));
1807                }
1808
1809                if let Err(e) = device.check_is_valid() {
1810                    break 'error e.into();
1811                }
1812
1813                let surface = self.surfaces.get(surface_id);
1814
1815                let caps = match surface.get_capabilities(&device.adapter) {
1816                    Ok(caps) => caps,
1817                    Err(_) => break 'error E::UnsupportedQueueFamily,
1818                };
1819
1820                let mut hal_view_formats = Vec::new();
1821                for format in config.view_formats.iter() {
1822                    if *format == config.format {
1823                        continue;
1824                    }
1825                    if !caps.formats.contains(&config.format) {
1826                        break 'error E::UnsupportedFormat {
1827                            requested: config.format,
1828                            available: caps.formats,
1829                        };
1830                    }
1831                    if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
1832                        break 'error E::InvalidViewFormat(*format, config.format);
1833                    }
1834                    hal_view_formats.push(*format);
1835                }
1836
1837                if !hal_view_formats.is_empty() {
1838                    if let Err(missing_flag) =
1839                        device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
1840                    {
1841                        break 'error E::MissingDownlevelFlags(missing_flag);
1842                    }
1843                }
1844
1845                let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
1846                    *caps.maximum_frame_latency.start(),
1847                    *caps.maximum_frame_latency.end(),
1848                );
1849                let mut hal_config = hal::SurfaceConfiguration {
1850                    maximum_frame_latency,
1851                    present_mode: config.present_mode,
1852                    composite_alpha_mode: config.alpha_mode,
1853                    format: config.format,
1854                    extent: wgt::Extent3d {
1855                        width: config.width,
1856                        height: config.height,
1857                        depth_or_array_layers: 1,
1858                    },
1859                    usage: conv::map_texture_usage(
1860                        config.usage,
1861                        hal::FormatAspects::COLOR,
1862                        wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
1863                            | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
1864                            | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1865                    ),
1866                    view_formats: hal_view_formats,
1867                };
1868
1869                if let Err(error) = validate_surface_configuration(
1870                    &mut hal_config,
1871                    &caps,
1872                    device.limits.max_texture_dimension_2d,
1873                ) {
1874                    break 'error error;
1875                }
1876
1877                // Wait for all work to finish before configuring the surface.
1878                let snatch_guard = device.snatchable_lock.read();
1879                let fence = device.fence.read();
1880
1881                let maintain_result;
1882                (user_callbacks, maintain_result) =
1883                    device.maintain(fence, wgt::PollType::Wait, snatch_guard);
1884
1885                match maintain_result {
1886                    // We're happy
1887                    Ok(wgt::PollStatus::QueueEmpty) => {}
1888                    Ok(wgt::PollStatus::WaitSucceeded) => {
1889                        // After the wait, the queue should be empty. It can only be non-empty
1890                        // if another thread is submitting at the same time.
1891                        break 'error E::GpuWaitTimeout;
1892                    }
1893                    Ok(wgt::PollStatus::Poll) => {
1894                        unreachable!("Cannot get a Poll result from a Wait action.")
1895                    }
1896                    Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
1897                        // On wasm, you cannot actually successfully wait for the surface.
1898                        // However WebGL does not actually require you do this, so ignoring
1899                        // the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363
1900                    }
1901                    Err(e) => {
1902                        break 'error e.into();
1903                    }
1904                }
1905
1906                // All textures must be destroyed before the surface can be re-configured.
1907                if let Some(present) = surface.presentation.lock().take() {
1908                    if present.acquired_texture.is_some() {
1909                        break 'error E::PreviousOutputExists;
1910                    }
1911                }
1912
1913                // TODO: Texture views may still be alive that point to the texture.
1914                // this will allow the user to render to the surface texture, long after
1915                // it has been removed.
1916                //
1917                // https://github.com/gfx-rs/wgpu/issues/4105
1918
1919                let surface_raw = surface.raw(device.backend()).unwrap();
1920                match unsafe { surface_raw.configure(device.raw(), &hal_config) } {
1921                    Ok(()) => (),
1922                    Err(error) => {
1923                        break 'error match error {
1924                            hal::SurfaceError::Outdated | hal::SurfaceError::Lost => {
1925                                E::InvalidSurface
1926                            }
1927                            hal::SurfaceError::Device(error) => {
1928                                E::Device(device.handle_hal_error(error))
1929                            }
1930                            hal::SurfaceError::Other(message) => {
1931                                log::error!("surface configuration failed: {}", message);
1932                                E::InvalidSurface
1933                            }
1934                        }
1935                    }
1936                }
1937
1938                let mut presentation = surface.presentation.lock();
1939                *presentation = Some(present::Presentation {
1940                    device,
1941                    config: config.clone(),
1942                    acquired_texture: None,
1943                });
1944            }
1945
1946            user_callbacks.fire();
1947            return None;
1948        };
1949
1950        Some(error)
1951    }
1952
1953    /// Check `device_id` for freeable resources and completed buffer mappings.
1954    ///
1955    /// Return `queue_empty` indicating whether there are more queue submissions still in flight.
1956    pub fn device_poll(
1957        &self,
1958        device_id: DeviceId,
1959        poll_type: wgt::PollType<crate::SubmissionIndex>,
1960    ) -> Result<wgt::PollStatus, WaitIdleError> {
1961        api_log!("Device::poll {poll_type:?}");
1962
1963        let device = self.hub.devices.get(device_id);
1964
1965        let (closures, result) = Self::poll_single_device(&device, poll_type);
1966
1967        closures.fire();
1968
1969        result
1970    }
1971
1972    fn poll_single_device(
1973        device: &crate::device::Device,
1974        poll_type: wgt::PollType<crate::SubmissionIndex>,
1975    ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {
1976        let snatch_guard = device.snatchable_lock.read();
1977        let fence = device.fence.read();
1978        let maintain_result = device.maintain(fence, poll_type, snatch_guard);
1979
1980        // Some deferred destroys are scheduled in maintain so run this right after
1981        // to avoid holding on to them until the next device poll.
1982        device.deferred_resource_destruction();
1983
1984        maintain_result
1985    }
1986
1987    /// Poll all devices belonging to the specified backend.
1988    ///
1989    /// If `force_wait` is true, block until all buffer mappings are done.
1990    ///
1991    /// Return `all_queue_empty` indicating whether there are more queue
1992    /// submissions still in flight.
1993    fn poll_all_devices_of_api(
1994        &self,
1995        force_wait: bool,
1996        closure_list: &mut UserClosures,
1997    ) -> Result<bool, WaitIdleError> {
1998        profiling::scope!("poll_device");
1999
2000        let hub = &self.hub;
2001        let mut all_queue_empty = true;
2002        {
2003            let device_guard = hub.devices.read();
2004
2005            for (_id, device) in device_guard.iter() {
2006                let poll_type = if force_wait {
2007                    wgt::PollType::Wait
2008                } else {
2009                    wgt::PollType::Poll
2010                };
2011
2012                let (closures, result) = Self::poll_single_device(device, poll_type);
2013
2014                let is_queue_empty = matches!(result, Ok(wgt::PollStatus::QueueEmpty));
2015
2016                all_queue_empty &= is_queue_empty;
2017
2018                closure_list.extend(closures);
2019            }
2020        }
2021
2022        Ok(all_queue_empty)
2023    }
2024
2025    /// Poll all devices on all backends.
2026    ///
2027    /// This is the implementation of `wgpu::Instance::poll_all`.
2028    ///
2029    /// Return `all_queue_empty` indicating whether there are more queue
2030    /// submissions still in flight.
2031    pub fn poll_all_devices(&self, force_wait: bool) -> Result<bool, WaitIdleError> {
2032        api_log!("poll_all_devices");
2033        let mut closures = UserClosures::default();
2034        let all_queue_empty = self.poll_all_devices_of_api(force_wait, &mut closures)?;
2035
2036        closures.fire();
2037
2038        Ok(all_queue_empty)
2039    }
2040
2041    /// # Safety
2042    ///
2043    /// - See [wgpu::Device::start_graphics_debugger_capture][api] for details the safety.
2044    ///
2045    /// [api]: ../../wgpu/struct.Device.html#method.start_graphics_debugger_capture
2046    pub unsafe fn device_start_graphics_debugger_capture(&self, device_id: DeviceId) {
2047        api_log!("Device::start_graphics_debugger_capture");
2048
2049        let device = self.hub.devices.get(device_id);
2050
2051        if !device.is_valid() {
2052            return;
2053        }
2054        unsafe { device.raw().start_graphics_debugger_capture() };
2055    }
2056
2057    /// # Safety
2058    ///
2059    /// - See [wgpu::Device::stop_graphics_debugger_capture][api] for details the safety.
2060    ///
2061    /// [api]: ../../wgpu/struct.Device.html#method.stop_graphics_debugger_capture
2062    pub unsafe fn device_stop_graphics_debugger_capture(&self, device_id: DeviceId) {
2063        api_log!("Device::stop_graphics_debugger_capture");
2064
2065        let device = self.hub.devices.get(device_id);
2066
2067        if !device.is_valid() {
2068            return;
2069        }
2070        unsafe { device.raw().stop_graphics_debugger_capture() };
2071    }
2072
2073    pub fn pipeline_cache_get_data(&self, id: id::PipelineCacheId) -> Option<Vec<u8>> {
2074        use crate::pipeline_cache;
2075        api_log!("PipelineCache::get_data");
2076        let hub = &self.hub;
2077
2078        if let Ok(cache) = hub.pipeline_caches.get(id).get() {
2079            // TODO: Is this check needed?
2080            if !cache.device.is_valid() {
2081                return None;
2082            }
2083            let mut vec = unsafe { cache.device.raw().pipeline_cache_get_data(cache.raw()) }?;
2084            let validation_key = cache.device.raw().pipeline_cache_validation_key()?;
2085
2086            let mut header_contents = [0; pipeline_cache::HEADER_LENGTH];
2087            pipeline_cache::add_cache_header(
2088                &mut header_contents,
2089                &vec,
2090                &cache.device.adapter.raw.info,
2091                validation_key,
2092            );
2093
2094            let deleted = vec.splice(..0, header_contents).collect::<Vec<_>>();
2095            debug_assert!(deleted.is_empty());
2096
2097            return Some(vec);
2098        }
2099        None
2100    }
2101
2102    pub fn device_drop(&self, device_id: DeviceId) {
2103        profiling::scope!("Device::drop");
2104        api_log!("Device::drop {device_id:?}");
2105
2106        self.hub.devices.remove(device_id);
2107    }
2108
2109    /// `device_lost_closure` might never be called.
2110    pub fn device_set_device_lost_closure(
2111        &self,
2112        device_id: DeviceId,
2113        device_lost_closure: DeviceLostClosure,
2114    ) {
2115        let device = self.hub.devices.get(device_id);
2116
2117        device
2118            .device_lost_closure
2119            .lock()
2120            .replace(device_lost_closure);
2121    }
2122
2123    pub fn device_destroy(&self, device_id: DeviceId) {
2124        api_log!("Device::destroy {device_id:?}");
2125
2126        let device = self.hub.devices.get(device_id);
2127
2128        // Follow the steps at
2129        // https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy.
2130        // It's legal to call destroy multiple times, but if the device
2131        // is already invalid, there's nothing more to do. There's also
2132        // no need to return an error.
2133        if !device.is_valid() {
2134            return;
2135        }
2136
2137        // The last part of destroy is to lose the device. The spec says
2138        // delay that until all "currently-enqueued operations on any
2139        // queue on this device are completed." This is accomplished by
2140        // setting valid to false, and then relying upon maintain to
2141        // check for empty queues and a DeviceLostClosure. At that time,
2142        // the DeviceLostClosure will be called with "destroyed" as the
2143        // reason.
2144        device.valid.store(false, Ordering::Release);
2145    }
2146
2147    pub fn device_get_internal_counters(&self, device_id: DeviceId) -> wgt::InternalCounters {
2148        let device = self.hub.devices.get(device_id);
2149        wgt::InternalCounters {
2150            hal: device.get_hal_counters(),
2151            core: wgt::CoreCounters {},
2152        }
2153    }
2154
2155    pub fn device_generate_allocator_report(
2156        &self,
2157        device_id: DeviceId,
2158    ) -> Option<wgt::AllocatorReport> {
2159        let device = self.hub.devices.get(device_id);
2160        device.generate_allocator_report()
2161    }
2162
2163    pub fn queue_drop(&self, queue_id: QueueId) {
2164        profiling::scope!("Queue::drop");
2165        api_log!("Queue::drop {queue_id:?}");
2166
2167        self.hub.queues.remove(queue_id);
2168    }
2169
2170    /// `op.callback` is guaranteed to be called.
2171    pub fn buffer_map_async(
2172        &self,
2173        buffer_id: id::BufferId,
2174        offset: BufferAddress,
2175        size: Option<BufferAddress>,
2176        op: BufferMapOperation,
2177    ) -> Result<crate::SubmissionIndex, BufferAccessError> {
2178        profiling::scope!("Buffer::map_async");
2179        api_log!("Buffer::map_async {buffer_id:?} offset {offset:?} size {size:?} op: {op:?}");
2180
2181        let hub = &self.hub;
2182
2183        let map_result = match hub.buffers.get(buffer_id).get() {
2184            Ok(buffer) => buffer.map_async(offset, size, op),
2185            Err(e) => Err((op, e.into())),
2186        };
2187
2188        match map_result {
2189            Ok(submission_index) => Ok(submission_index),
2190            Err((mut operation, err)) => {
2191                if let Some(callback) = operation.callback.take() {
2192                    callback(Err(err.clone()));
2193                }
2194                Err(err)
2195            }
2196        }
2197    }
2198
2199    pub fn buffer_get_mapped_range(
2200        &self,
2201        buffer_id: id::BufferId,
2202        offset: BufferAddress,
2203        size: Option<BufferAddress>,
2204    ) -> Result<(NonNull<u8>, u64), BufferAccessError> {
2205        profiling::scope!("Buffer::get_mapped_range");
2206        api_log!("Buffer::get_mapped_range {buffer_id:?} offset {offset:?} size {size:?}");
2207
2208        let hub = &self.hub;
2209
2210        let buffer = hub.buffers.get(buffer_id).get()?;
2211
2212        {
2213            let snatch_guard = buffer.device.snatchable_lock.read();
2214            buffer.check_destroyed(&snatch_guard)?;
2215        }
2216
2217        let range_size = if let Some(size) = size {
2218            size
2219        } else {
2220            buffer.size.saturating_sub(offset)
2221        };
2222
2223        if offset % wgt::MAP_ALIGNMENT != 0 {
2224            return Err(BufferAccessError::UnalignedOffset { offset });
2225        }
2226        if range_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
2227            return Err(BufferAccessError::UnalignedRangeSize { range_size });
2228        }
2229        let map_state = &*buffer.map_state.lock();
2230        match *map_state {
2231            resource::BufferMapState::Init { ref staging_buffer } => {
2232                // offset (u64) can not be < 0, so no need to validate the lower bound
2233                if offset + range_size > buffer.size {
2234                    return Err(BufferAccessError::OutOfBoundsOverrun {
2235                        index: offset + range_size - 1,
2236                        max: buffer.size,
2237                    });
2238                }
2239                let ptr = unsafe { staging_buffer.ptr() };
2240                let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) };
2241                Ok((ptr, range_size))
2242            }
2243            resource::BufferMapState::Active {
2244                ref mapping,
2245                ref range,
2246                ..
2247            } => {
2248                if offset < range.start {
2249                    return Err(BufferAccessError::OutOfBoundsUnderrun {
2250                        index: offset,
2251                        min: range.start,
2252                    });
2253                }
2254                if offset + range_size > range.end {
2255                    return Err(BufferAccessError::OutOfBoundsOverrun {
2256                        index: offset + range_size - 1,
2257                        max: range.end,
2258                    });
2259                }
2260                // ptr points to the beginning of the range we mapped in map_async
2261                // rather than the beginning of the buffer.
2262                let relative_offset = (offset - range.start) as isize;
2263                unsafe {
2264                    Ok((
2265                        NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)),
2266                        range_size,
2267                    ))
2268                }
2269            }
2270            resource::BufferMapState::Idle | resource::BufferMapState::Waiting(_) => {
2271                Err(BufferAccessError::NotMapped)
2272            }
2273        }
2274    }
2275    pub fn buffer_unmap(&self, buffer_id: id::BufferId) -> BufferAccessResult {
2276        profiling::scope!("unmap", "Buffer");
2277        api_log!("Buffer::unmap {buffer_id:?}");
2278
2279        let hub = &self.hub;
2280
2281        let buffer = hub.buffers.get(buffer_id).get()?;
2282
2283        let snatch_guard = buffer.device.snatchable_lock.read();
2284        buffer.check_destroyed(&snatch_guard)?;
2285        drop(snatch_guard);
2286
2287        buffer.device.check_is_valid()?;
2288        buffer.unmap(
2289            #[cfg(feature = "trace")]
2290            buffer_id,
2291        )
2292    }
2293}