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