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