wgpu_core/device/
global.rs

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