wgpu_core/device/
resource.rs

1use alloc::{
2    borrow::Cow,
3    boxed::Box,
4    string::{String, ToString as _},
5    sync::{Arc, Weak},
6    vec::Vec,
7};
8use core::{
9    fmt,
10    mem::{self, ManuallyDrop},
11    num::NonZeroU32,
12    sync::atomic::{AtomicBool, Ordering},
13};
14use hal::ShouldBeNonZeroExt;
15
16use arrayvec::ArrayVec;
17use bitflags::Flags;
18use smallvec::SmallVec;
19use wgt::{
20    math::align_to, DeviceLostReason, TextureFormat, TextureSampleType, TextureSelector,
21    TextureViewDimension,
22};
23
24#[cfg(feature = "trace")]
25use crate::device::trace;
26use crate::{
27    api_log,
28    binding_model::{
29        self, BindGroup, BindGroupLateBufferBindingInfo, BindGroupLayout, BindGroupLayoutEntryError,
30    },
31    command, conv,
32    device::{
33        bgl, create_validator, features_to_naga_capabilities, life::WaitIdleError, map_buffer,
34        AttachmentData, DeviceLostInvocation, HostMap, MissingDownlevelFlags, MissingFeatures,
35        RenderPassContext,
36    },
37    hal_label,
38    init_tracker::{
39        BufferInitTracker, BufferInitTrackerAction, MemoryInitKind, TextureInitRange,
40        TextureInitTrackerAction,
41    },
42    instance::{Adapter, RequestDeviceError},
43    lock::{rank, Mutex, RwLock},
44    pipeline,
45    pool::ResourcePool,
46    present,
47    resource::{
48        self, Buffer, ExternalTexture, Fallible, Labeled, ParentDevice, QuerySet,
49        RawResourceAccess, Sampler, StagingBuffer, Texture, TextureView,
50        TextureViewNotRenderableReason, Tlas, TrackingData,
51    },
52    resource_log,
53    snatch::{SnatchGuard, SnatchLock, Snatchable},
54    timestamp_normalization::TIMESTAMP_NORMALIZATION_BUFFER_USES,
55    track::{BindGroupStates, DeviceTracker, TrackerIndexAllocators, UsageScope, UsageScopePool},
56    validation,
57    weak_vec::WeakVec,
58    FastHashMap, LabelHelpers, OnceCellOrLock,
59};
60
61use super::{
62    queue::Queue, DeviceDescriptor, DeviceError, DeviceLostClosure, UserClosures,
63    ENTRYPOINT_FAILURE_ERROR, ZERO_BUFFER_SIZE,
64};
65
66#[cfg(supports_64bit_atomics)]
67use core::sync::atomic::AtomicU64;
68#[cfg(not(supports_64bit_atomics))]
69use portable_atomic::AtomicU64;
70
71pub(crate) struct CommandIndices {
72    /// The index of the last command submission that was attempted.
73    ///
74    /// Note that `fence` may never be signalled with this value, if the command
75    /// submission failed. If you need to wait for everything running on a
76    /// `Queue` to complete, wait for [`last_successful_submission_index`].
77    ///
78    /// [`last_successful_submission_index`]: Device::last_successful_submission_index
79    pub(crate) active_submission_index: hal::FenceValue,
80    pub(crate) next_acceleration_structure_build_command_index: u64,
81}
82
83/// Parameters provided to shaders via a uniform buffer of the type
84/// [`NagaExternalTextureParams`], describing an [`ExternalTexture`] resource
85/// binding.
86///
87/// [`NagaExternalTextureParams`]: naga::SpecialTypes::external_texture_params
88/// [`ExternalTexture`]: binding_model::BindingResource::ExternalTexture
89#[repr(C)]
90#[derive(Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]
91pub struct ExternalTextureParams {
92    /// 4x4 column-major matrix with which to convert sampled YCbCr values
93    /// to RGBA.
94    ///
95    /// This is ignored when `num_planes` is 1.
96    pub yuv_conversion_matrix: [f32; 16],
97
98    /// 3x3 column-major matrix to transform linear RGB values in the source
99    /// color space to linear RGB values in the destination color space. In
100    /// combination with [`Self::src_transfer_function`] and
101    /// [`Self::dst_transfer_function`] this can be used to ensure that
102    /// [`ImageSample`] and [`ImageLoad`] operations return values in the
103    /// desired destination color space rather than the source color space of
104    /// the underlying planes.
105    ///
106    /// Includes a padding element after each column.
107    ///
108    /// [`ImageSample`]: naga::ir::Expression::ImageSample
109    /// [`ImageLoad`]: naga::ir::Expression::ImageLoad
110    pub gamut_conversion_matrix: [f32; 12],
111
112    /// Transfer function for the source color space. The *inverse* of this
113    /// will be applied to decode non-linear RGB to linear RGB in the source
114    /// color space.
115    pub src_transfer_function: wgt::ExternalTextureTransferFunction,
116
117    /// Transfer function for the destination color space. This will be applied
118    /// to encode linear RGB to non-linear RGB in the destination color space.
119    pub dst_transfer_function: wgt::ExternalTextureTransferFunction,
120
121    /// Transform to apply to [`ImageSample`] coordinates.
122    ///
123    /// This is a 3x2 column-major matrix representing an affine transform from
124    /// normalized texture coordinates to the normalized coordinates that should
125    /// be sampled from the external texture's underlying plane(s).
126    ///
127    /// This transform may scale, translate, flip, and rotate in 90-degree
128    /// increments, but the result of transforming the rectangle (0,0)..(1,1)
129    /// must be an axis-aligned rectangle that falls within the bounds of
130    /// (0,0)..(1,1).
131    ///
132    /// [`ImageSample`]: naga::ir::Expression::ImageSample
133    pub sample_transform: [f32; 6],
134
135    /// Transform to apply to [`ImageLoad`] coordinates.
136    ///
137    /// This is a 3x2 column-major matrix representing an affine transform from
138    /// non-normalized texel coordinates to the non-normalized coordinates of
139    /// the texel that should be loaded from the external texture's underlying
140    /// plane 0. For planes 1 and 2, if present, plane 0's coordinates are
141    /// scaled according to the textures' relative sizes.
142    ///
143    /// This transform may scale, translate, flip, and rotate in 90-degree
144    /// increments, but the result of transforming the rectangle (0,0)..[`size`]
145    /// must be an axis-aligned rectangle that falls within the bounds of
146    /// (0,0)..[`size`].
147    ///
148    /// [`ImageLoad`]: naga::ir::Expression::ImageLoad
149    /// [`size`]: Self::size
150    pub load_transform: [f32; 6],
151
152    /// Size of the external texture.
153    ///
154    /// This is the value that should be returned by size queries in shader
155    /// code; it does not necessarily match the dimensions of the underlying
156    /// texture(s). As a special case, if this is `[0, 0]`, the actual size of
157    /// plane 0 should be used instead.
158    ///
159    /// This must be consistent with [`sample_transform`]: it should be the size
160    /// in texels of the rectangle covered by the square (0,0)..(1,1) after
161    /// [`sample_transform`] has been applied to it.
162    ///
163    /// [`sample_transform`]: Self::sample_transform
164    pub size: [u32; 2],
165
166    /// Number of planes. 1 indicates a single RGBA plane. 2 indicates a Y
167    /// plane and an interleaved CbCr plane. 3 indicates separate Y, Cb, and Cr
168    /// planes.
169    pub num_planes: u32,
170    // Ensure the size of this struct matches the type generated by Naga.
171    pub _padding: [u8; 4],
172}
173
174impl ExternalTextureParams {
175    pub fn from_desc<L>(desc: &wgt::ExternalTextureDescriptor<L>) -> Self {
176        let gamut_conversion_matrix = [
177            desc.gamut_conversion_matrix[0],
178            desc.gamut_conversion_matrix[1],
179            desc.gamut_conversion_matrix[2],
180            0.0, // padding
181            desc.gamut_conversion_matrix[3],
182            desc.gamut_conversion_matrix[4],
183            desc.gamut_conversion_matrix[5],
184            0.0, // padding
185            desc.gamut_conversion_matrix[6],
186            desc.gamut_conversion_matrix[7],
187            desc.gamut_conversion_matrix[8],
188            0.0, // padding
189        ];
190
191        Self {
192            yuv_conversion_matrix: desc.yuv_conversion_matrix,
193            gamut_conversion_matrix,
194            src_transfer_function: desc.src_transfer_function,
195            dst_transfer_function: desc.dst_transfer_function,
196            size: [desc.width, desc.height],
197            sample_transform: desc.sample_transform,
198            load_transform: desc.load_transform,
199            num_planes: desc.num_planes() as u32,
200            _padding: Default::default(),
201        }
202    }
203}
204
205/// Structure describing a logical device. Some members are internally mutable,
206/// stored behind mutexes.
207pub struct Device {
208    raw: Box<dyn hal::DynDevice>,
209    pub(crate) adapter: Arc<Adapter>,
210    pub(crate) queue: OnceCellOrLock<Weak<Queue>>,
211    pub(crate) zero_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
212    pub(crate) empty_bgl: ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>,
213    /// The `label` from the descriptor used to create the resource.
214    label: String,
215
216    pub(crate) command_allocator: command::CommandAllocator,
217
218    pub(crate) command_indices: RwLock<CommandIndices>,
219
220    /// The index of the last successful submission to this device's
221    /// [`hal::Queue`].
222    ///
223    /// Unlike [`active_submission_index`], which is incremented each time
224    /// submission is attempted, this is updated only when submission succeeds,
225    /// so waiting for this value won't hang waiting for work that was never
226    /// submitted.
227    ///
228    /// [`active_submission_index`]: CommandIndices::active_submission_index
229    pub(crate) last_successful_submission_index: hal::AtomicFenceValue,
230
231    // NOTE: if both are needed, the `snatchable_lock` must be consistently acquired before the
232    // `fence` lock to avoid deadlocks.
233    pub(crate) fence: RwLock<ManuallyDrop<Box<dyn hal::DynFence>>>,
234    pub(crate) snatchable_lock: SnatchLock,
235
236    /// Is this device valid? Valid is closely associated with "lose the device",
237    /// which can be triggered by various methods, including at the end of device
238    /// destroy, and by any GPU errors that cause us to no longer trust the state
239    /// of the device. Ideally we would like to fold valid into the storage of
240    /// the device itself (for example as an Error enum), but unfortunately we
241    /// need to continue to be able to retrieve the device in poll_devices to
242    /// determine if it can be dropped. If our internal accesses of devices were
243    /// done through ref-counted references and external accesses checked for
244    /// Error enums, we wouldn't need this. For now, we need it. All the call
245    /// sites where we check it are areas that should be revisited if we start
246    /// using ref-counted references for internal access.
247    pub(crate) valid: AtomicBool,
248
249    /// Closure to be called on "lose the device". This is invoked directly by
250    /// device.lose or by the UserCallbacks returned from maintain when the device
251    /// has been destroyed and its queues are empty.
252    pub(crate) device_lost_closure: Mutex<Option<DeviceLostClosure>>,
253
254    /// Stores the state of buffers and textures.
255    pub(crate) trackers: Mutex<DeviceTracker>,
256    pub(crate) tracker_indices: TrackerIndexAllocators,
257    /// Pool of bind group layouts, allowing deduplication.
258    pub(crate) bgl_pool: ResourcePool<bgl::EntryMap, BindGroupLayout>,
259    pub(crate) alignments: hal::Alignments,
260    pub(crate) limits: wgt::Limits,
261    pub(crate) features: wgt::Features,
262    pub(crate) downlevel: wgt::DownlevelCapabilities,
263    pub(crate) instance_flags: wgt::InstanceFlags,
264    pub(crate) deferred_destroy: Mutex<Vec<DeferredDestroy>>,
265    pub(crate) usage_scopes: UsageScopePool,
266    pub(crate) indirect_validation: Option<crate::indirect_validation::IndirectValidation>,
267    // Optional so that we can late-initialize this after the queue is created.
268    pub(crate) timestamp_normalizer:
269        OnceCellOrLock<crate::timestamp_normalization::TimestampNormalizer>,
270    /// Uniform buffer containing [`ExternalTextureParams`] with values such
271    /// that a [`TextureView`] bound to a [`wgt::BindingType::ExternalTexture`]
272    /// binding point will be rendered correctly. Intended to be used as the
273    /// [`hal::ExternalTextureBinding::params`] field.
274    pub(crate) default_external_texture_params_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
275    // needs to be dropped last
276    #[cfg(feature = "trace")]
277    pub(crate) trace: Mutex<Option<Box<dyn trace::Trace + Send + Sync + 'static>>>,
278}
279
280pub(crate) enum DeferredDestroy {
281    TextureViews(WeakVec<TextureView>),
282    BindGroups(WeakVec<BindGroup>),
283}
284
285impl fmt::Debug for Device {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        f.debug_struct("Device")
288            .field("label", &self.label())
289            .field("limits", &self.limits)
290            .field("features", &self.features)
291            .field("downlevel", &self.downlevel)
292            .finish()
293    }
294}
295
296impl Drop for Device {
297    fn drop(&mut self) {
298        resource_log!("Drop {}", self.error_ident());
299
300        // SAFETY: We are in the Drop impl and we don't use self.zero_buffer anymore after this point.
301        let zero_buffer = unsafe { ManuallyDrop::take(&mut self.zero_buffer) };
302        // SAFETY: We are in the Drop impl and we don't use self.empty_bgl anymore after this point.
303        let empty_bgl = unsafe { ManuallyDrop::take(&mut self.empty_bgl) };
304        // SAFETY: We are in the Drop impl and we don't use
305        // self.default_external_texture_params_buffer anymore after this point.
306        let default_external_texture_params_buffer =
307            unsafe { ManuallyDrop::take(&mut self.default_external_texture_params_buffer) };
308        // SAFETY: We are in the Drop impl and we don't use self.fence anymore after this point.
309        let fence = unsafe { ManuallyDrop::take(&mut self.fence.write()) };
310        if let Some(indirect_validation) = self.indirect_validation.take() {
311            indirect_validation.dispose(self.raw.as_ref());
312        }
313        if let Some(timestamp_normalizer) = self.timestamp_normalizer.take() {
314            timestamp_normalizer.dispose(self.raw.as_ref());
315        }
316        unsafe {
317            self.raw.destroy_buffer(zero_buffer);
318            self.raw.destroy_bind_group_layout(empty_bgl);
319            self.raw
320                .destroy_buffer(default_external_texture_params_buffer);
321            self.raw.destroy_fence(fence);
322        }
323    }
324}
325
326impl Device {
327    pub(crate) fn raw(&self) -> &dyn hal::DynDevice {
328        self.raw.as_ref()
329    }
330    pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {
331        if self.features.contains(feature) {
332            Ok(())
333        } else {
334            Err(MissingFeatures(feature))
335        }
336    }
337
338    pub(crate) fn require_downlevel_flags(
339        &self,
340        flags: wgt::DownlevelFlags,
341    ) -> Result<(), MissingDownlevelFlags> {
342        if self.downlevel.flags.contains(flags) {
343            Ok(())
344        } else {
345            Err(MissingDownlevelFlags(flags))
346        }
347    }
348
349    /// # Safety
350    ///
351    /// - See [wgpu::Device::start_graphics_debugger_capture][api] for details the safety.
352    ///
353    /// [api]: ../../wgpu/struct.Device.html#method.start_graphics_debugger_capture
354    pub unsafe fn start_graphics_debugger_capture(&self) {
355        api_log!("Device::start_graphics_debugger_capture");
356
357        if !self.is_valid() {
358            return;
359        }
360        unsafe { self.raw().start_graphics_debugger_capture() };
361    }
362
363    /// # Safety
364    ///
365    /// - See [wgpu::Device::stop_graphics_debugger_capture][api] for details the safety.
366    ///
367    /// [api]: ../../wgpu/struct.Device.html#method.stop_graphics_debugger_capture
368    pub unsafe fn stop_graphics_debugger_capture(&self) {
369        api_log!("Device::stop_graphics_debugger_capture");
370
371        if !self.is_valid() {
372            return;
373        }
374        unsafe { self.raw().stop_graphics_debugger_capture() };
375    }
376}
377
378impl Device {
379    pub(crate) fn new(
380        raw_device: Box<dyn hal::DynDevice>,
381        adapter: &Arc<Adapter>,
382        desc: &DeviceDescriptor,
383        instance_flags: wgt::InstanceFlags,
384    ) -> Result<Self, DeviceError> {
385        #[cfg(not(feature = "trace"))]
386        match &desc.trace {
387            wgt::Trace::Off => {}
388            _ => {
389                log::error!("wgpu-core feature 'trace' is not enabled");
390            }
391        };
392        #[cfg(feature = "trace")]
393        let trace: Option<Box<dyn trace::Trace + Send + Sync + 'static>> = match &desc.trace {
394            wgt::Trace::Off => None,
395            wgt::Trace::Directory(dir) => match trace::DiskTrace::new(dir.clone()) {
396                Ok(mut trace) => {
397                    trace::Trace::add(
398                        &mut trace,
399                        trace::Action::Init {
400                            desc: wgt::DeviceDescriptor {
401                                trace: wgt::Trace::Off,
402                                ..desc.clone()
403                            },
404                            backend: adapter.backend(),
405                        },
406                    );
407                    Some(Box::new(trace))
408                }
409                Err(e) => {
410                    log::error!("Unable to start a trace in '{dir:?}': {e}");
411                    None
412                }
413            },
414            wgt::Trace::Memory => {
415                let mut trace = trace::MemoryTrace::new();
416                trace::Trace::add(
417                    &mut trace,
418                    trace::Action::Init {
419                        desc: wgt::DeviceDescriptor {
420                            trace: wgt::Trace::Off,
421                            ..desc.clone()
422                        },
423                        backend: adapter.backend(),
424                    },
425                );
426                Some(Box::new(trace))
427            }
428            // The enum is non_exhaustive, so we must have a fallback arm (that should be
429            // unreachable in practice).
430            t => {
431                log::error!("unimplemented wgpu_types::Trace variant {t:?}");
432                None
433            }
434        };
435
436        let fence = unsafe { raw_device.create_fence() }.map_err(DeviceError::from_hal)?;
437
438        let command_allocator = command::CommandAllocator::new();
439
440        let rt_uses = if desc
441            .required_features
442            .intersects(wgt::Features::EXPERIMENTAL_RAY_QUERY)
443        {
444            wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT
445        } else {
446            wgt::BufferUses::empty()
447        };
448
449        // Create zeroed buffer used for texture clears (and raytracing if required).
450        let zero_buffer = unsafe {
451            raw_device.create_buffer(&hal::BufferDescriptor {
452                label: hal_label(Some("(wgpu internal) zero init buffer"), instance_flags),
453                size: ZERO_BUFFER_SIZE,
454                usage: wgt::BufferUses::COPY_SRC | wgt::BufferUses::COPY_DST | rt_uses,
455                memory_flags: hal::MemoryFlags::empty(),
456            })
457        }
458        .map_err(DeviceError::from_hal)?;
459
460        let empty_bgl = unsafe {
461            raw_device.create_bind_group_layout(&hal::BindGroupLayoutDescriptor {
462                label: None,
463                flags: hal::BindGroupLayoutFlags::empty(),
464                entries: &[],
465            })
466        }
467        .map_err(DeviceError::from_hal)?;
468
469        let default_external_texture_params_buffer = unsafe {
470            raw_device.create_buffer(&hal::BufferDescriptor {
471                label: hal_label(
472                    Some("(wgpu internal) default external texture params buffer"),
473                    instance_flags,
474                ),
475                size: size_of::<ExternalTextureParams>() as _,
476                usage: wgt::BufferUses::COPY_DST | wgt::BufferUses::UNIFORM,
477                memory_flags: hal::MemoryFlags::empty(),
478            })
479        }
480        .map_err(DeviceError::from_hal)?;
481
482        // Cloned as we need them below anyway.
483        let alignments = adapter.raw.capabilities.alignments.clone();
484        let downlevel = adapter.raw.capabilities.downlevel.clone();
485        let limits = &adapter.raw.capabilities.limits;
486
487        let enable_indirect_validation = instance_flags
488            .contains(wgt::InstanceFlags::VALIDATION_INDIRECT_CALL)
489            && downlevel.flags.contains(
490                wgt::DownlevelFlags::INDIRECT_EXECUTION | wgt::DownlevelFlags::COMPUTE_SHADERS,
491            )
492            && limits.max_storage_buffers_per_shader_stage >= 2;
493
494        let indirect_validation = if enable_indirect_validation {
495            Some(crate::indirect_validation::IndirectValidation::new(
496                raw_device.as_ref(),
497                &desc.required_limits,
498                &desc.required_features,
499                instance_flags,
500                adapter.backend(),
501            )?)
502        } else {
503            None
504        };
505
506        Ok(Self {
507            raw: raw_device,
508            adapter: adapter.clone(),
509            queue: OnceCellOrLock::new(),
510            zero_buffer: ManuallyDrop::new(zero_buffer),
511            empty_bgl: ManuallyDrop::new(empty_bgl),
512            default_external_texture_params_buffer: ManuallyDrop::new(
513                default_external_texture_params_buffer,
514            ),
515            label: desc.label.to_string(),
516            command_allocator,
517            command_indices: RwLock::new(
518                rank::DEVICE_COMMAND_INDICES,
519                CommandIndices {
520                    active_submission_index: 0,
521                    // By starting at one, we can put the result in a NonZeroU64.
522                    next_acceleration_structure_build_command_index: 1,
523                },
524            ),
525            last_successful_submission_index: AtomicU64::new(0),
526            fence: RwLock::new(rank::DEVICE_FENCE, ManuallyDrop::new(fence)),
527            snatchable_lock: unsafe { SnatchLock::new(rank::DEVICE_SNATCHABLE_LOCK) },
528            valid: AtomicBool::new(true),
529            device_lost_closure: Mutex::new(rank::DEVICE_LOST_CLOSURE, None),
530            trackers: Mutex::new(rank::DEVICE_TRACKERS, DeviceTracker::new()),
531            tracker_indices: TrackerIndexAllocators::new(),
532            bgl_pool: ResourcePool::new(),
533            #[cfg(feature = "trace")]
534            trace: Mutex::new(rank::DEVICE_TRACE, trace),
535            alignments,
536            limits: desc.required_limits.clone(),
537            features: desc.required_features,
538            downlevel,
539            instance_flags,
540            deferred_destroy: Mutex::new(rank::DEVICE_DEFERRED_DESTROY, Vec::new()),
541            usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()),
542            timestamp_normalizer: OnceCellOrLock::new(),
543            indirect_validation,
544        })
545    }
546
547    /// Initializes [`Device::default_external_texture_params_buffer`] with
548    /// required values such that a [`TextureView`] bound to a
549    /// [`wgt::BindingType::ExternalTexture`] binding point will be rendered
550    /// correctly.
551    fn init_default_external_texture_params_buffer(self: &Arc<Self>) -> Result<(), DeviceError> {
552        let data = ExternalTextureParams {
553            #[rustfmt::skip]
554            yuv_conversion_matrix: [
555                1.0, 0.0, 0.0, 0.0,
556                0.0, 1.0, 0.0, 0.0,
557                0.0, 0.0, 1.0, 0.0,
558                0.0, 0.0, 0.0, 1.0,
559            ],
560            #[rustfmt::skip]
561            gamut_conversion_matrix: [
562                1.0, 0.0, 0.0, /* padding */ 0.0,
563                0.0, 1.0, 0.0, /* padding */ 0.0,
564                0.0, 0.0, 1.0, /* padding */ 0.0,
565            ],
566            src_transfer_function: Default::default(),
567            dst_transfer_function: Default::default(),
568            size: [0, 0],
569            #[rustfmt::skip]
570            sample_transform: [
571                1.0, 0.0,
572                0.0, 1.0,
573                0.0, 0.0
574            ],
575            #[rustfmt::skip]
576            load_transform: [
577                1.0, 0.0,
578                0.0, 1.0,
579                0.0, 0.0
580            ],
581            num_planes: 1,
582            _padding: Default::default(),
583        };
584        let mut staging_buffer =
585            StagingBuffer::new(self, wgt::BufferSize::new(size_of_val(&data) as _).unwrap())?;
586        staging_buffer.write(bytemuck::bytes_of(&data));
587        let staging_buffer = staging_buffer.flush();
588
589        let params_buffer = self.default_external_texture_params_buffer.as_ref();
590        let queue = self.get_queue().unwrap();
591        let mut pending_writes = queue.pending_writes.lock();
592
593        unsafe {
594            pending_writes
595                .command_encoder
596                .transition_buffers(&[hal::BufferBarrier {
597                    buffer: params_buffer,
598                    usage: hal::StateTransition {
599                        from: wgt::BufferUses::MAP_WRITE,
600                        to: wgt::BufferUses::COPY_DST,
601                    },
602                }]);
603            pending_writes.command_encoder.copy_buffer_to_buffer(
604                staging_buffer.raw(),
605                params_buffer,
606                &[hal::BufferCopy {
607                    src_offset: 0,
608                    dst_offset: 0,
609                    size: staging_buffer.size,
610                }],
611            );
612            pending_writes.consume(staging_buffer);
613            pending_writes
614                .command_encoder
615                .transition_buffers(&[hal::BufferBarrier {
616                    buffer: params_buffer,
617                    usage: hal::StateTransition {
618                        from: wgt::BufferUses::COPY_DST,
619                        to: wgt::BufferUses::UNIFORM,
620                    },
621                }]);
622        }
623
624        Ok(())
625    }
626
627    pub fn late_init_resources_with_queue(self: &Arc<Self>) -> Result<(), RequestDeviceError> {
628        let queue = self.get_queue().unwrap();
629
630        let timestamp_normalizer = crate::timestamp_normalization::TimestampNormalizer::new(
631            self,
632            queue.get_timestamp_period(),
633        )?;
634
635        self.timestamp_normalizer
636            .set(timestamp_normalizer)
637            .unwrap_or_else(|_| panic!("Called late_init_resources_with_queue twice"));
638
639        self.init_default_external_texture_params_buffer()?;
640
641        Ok(())
642    }
643
644    /// Returns the backend this device is using.
645    pub fn backend(&self) -> wgt::Backend {
646        self.adapter.backend()
647    }
648
649    pub fn is_valid(&self) -> bool {
650        self.valid.load(Ordering::Acquire)
651    }
652
653    pub fn check_is_valid(&self) -> Result<(), DeviceError> {
654        if self.is_valid() {
655            Ok(())
656        } else {
657            Err(DeviceError::Lost)
658        }
659    }
660
661    /// Stop tracing and return the trace object.
662    ///
663    /// This is mostly useful for in-memory traces.
664    #[cfg(feature = "trace")]
665    pub fn take_trace(&self) -> Option<Box<dyn trace::Trace + Send + Sync + 'static>> {
666        self.trace.lock().take()
667    }
668
669    /// Checks that we are operating within the memory budget reported by the native APIs.
670    ///
671    /// If we are not, the device gets invalidated.
672    ///
673    /// The budget might fluctuate over the lifetime of the application, so it should be checked
674    /// somewhat frequently.
675    pub fn lose_if_oom(&self) {
676        let _ = self
677            .raw()
678            .check_if_oom()
679            .map_err(|e| self.handle_hal_error(e));
680    }
681
682    pub fn handle_hal_error(&self, error: hal::DeviceError) -> DeviceError {
683        match error {
684            hal::DeviceError::OutOfMemory
685            | hal::DeviceError::Lost
686            | hal::DeviceError::Unexpected => {
687                self.lose(&error.to_string());
688            }
689        }
690        DeviceError::from_hal(error)
691    }
692
693    pub fn handle_hal_error_with_nonfatal_oom(&self, error: hal::DeviceError) -> DeviceError {
694        match error {
695            hal::DeviceError::OutOfMemory => DeviceError::from_hal(error),
696            error => self.handle_hal_error(error),
697        }
698    }
699
700    /// Run some destroy operations that were deferred.
701    ///
702    /// Destroying the resources requires taking a write lock on the device's snatch lock,
703    /// so a good reason for deferring resource destruction is when we don't know for sure
704    /// how risky it is to take the lock (typically, it shouldn't be taken from the drop
705    /// implementation of a reference-counted structure).
706    /// The snatch lock must not be held while this function is called.
707    pub(crate) fn deferred_resource_destruction(&self) {
708        let deferred_destroy = mem::take(&mut *self.deferred_destroy.lock());
709        for item in deferred_destroy {
710            match item {
711                DeferredDestroy::TextureViews(views) => {
712                    for view in views {
713                        let Some(view) = view.upgrade() else {
714                            continue;
715                        };
716                        let Some(raw_view) = view.raw.snatch(&mut self.snatchable_lock.write())
717                        else {
718                            continue;
719                        };
720
721                        resource_log!("Destroy raw {}", view.error_ident());
722
723                        unsafe {
724                            self.raw().destroy_texture_view(raw_view);
725                        }
726                    }
727                }
728                DeferredDestroy::BindGroups(bind_groups) => {
729                    for bind_group in bind_groups {
730                        let Some(bind_group) = bind_group.upgrade() else {
731                            continue;
732                        };
733                        let Some(raw_bind_group) =
734                            bind_group.raw.snatch(&mut self.snatchable_lock.write())
735                        else {
736                            continue;
737                        };
738
739                        resource_log!("Destroy raw {}", bind_group.error_ident());
740
741                        unsafe {
742                            self.raw().destroy_bind_group(raw_bind_group);
743                        }
744                    }
745                }
746            }
747        }
748    }
749
750    pub fn get_queue(&self) -> Option<Arc<Queue>> {
751        self.queue.get().as_ref()?.upgrade()
752    }
753
754    pub fn set_queue(&self, queue: &Arc<Queue>) {
755        assert!(self.queue.set(Arc::downgrade(queue)).is_ok());
756    }
757
758    pub fn poll(
759        &self,
760        poll_type: wgt::PollType<crate::SubmissionIndex>,
761    ) -> Result<wgt::PollStatus, WaitIdleError> {
762        let (user_closures, result) = self.poll_and_return_closures(poll_type);
763        user_closures.fire();
764        result
765    }
766
767    /// Poll the device, returning any `UserClosures` that need to be executed.
768    ///
769    /// The caller must invoke the `UserClosures` even if this function returns
770    /// an error. This is an internal helper, used by `Device::poll` and
771    /// `Global::poll_all_devices`, so that `poll_all_devices` can invoke
772    /// closures once after all devices have been polled.
773    pub(crate) fn poll_and_return_closures(
774        &self,
775        poll_type: wgt::PollType<crate::SubmissionIndex>,
776    ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {
777        let snatch_guard = self.snatchable_lock.read();
778        let fence = self.fence.read();
779        let maintain_result = self.maintain(fence, poll_type, snatch_guard);
780
781        self.lose_if_oom();
782
783        // Some deferred destroys are scheduled in maintain so run this right after
784        // to avoid holding on to them until the next device poll.
785        self.deferred_resource_destruction();
786
787        maintain_result
788    }
789
790    /// Check the current status of the GPU and process any submissions that have
791    /// finished.
792    ///
793    /// The `poll_type` argument tells if this function should wait for a particular
794    /// submission index to complete, or if it should just poll the current status.
795    ///
796    /// This will process _all_ completed submissions, even if the caller only asked
797    /// us to poll to a given submission index.
798    ///
799    /// Return a pair `(closures, result)`, where:
800    ///
801    /// - `closures` is a list of callbacks that need to be invoked informing the user
802    ///   about various things occurring. These happen and should be handled even if
803    ///   this function returns an error, hence they are outside of the result.
804    ///
805    /// - `results` is a boolean indicating the result of the wait operation, including
806    ///   if there was a timeout or a validation error.
807    pub(crate) fn maintain<'this>(
808        &'this self,
809        fence: crate::lock::RwLockReadGuard<ManuallyDrop<Box<dyn hal::DynFence>>>,
810        poll_type: wgt::PollType<crate::SubmissionIndex>,
811        snatch_guard: SnatchGuard,
812    ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {
813        profiling::scope!("Device::maintain");
814
815        let mut user_closures = UserClosures::default();
816
817        // If a wait was requested, determine which submission index to wait for.
818        let wait_submission_index = match poll_type {
819            wgt::PollType::Wait {
820                submission_index: Some(submission_index),
821                ..
822            } => {
823                let last_successful_submission_index = self
824                    .last_successful_submission_index
825                    .load(Ordering::Acquire);
826
827                if submission_index > last_successful_submission_index {
828                    let result = Err(WaitIdleError::WrongSubmissionIndex(
829                        submission_index,
830                        last_successful_submission_index,
831                    ));
832
833                    return (user_closures, result);
834                }
835
836                Some(submission_index)
837            }
838            wgt::PollType::Wait {
839                submission_index: None,
840                ..
841            } => Some(
842                self.last_successful_submission_index
843                    .load(Ordering::Acquire),
844            ),
845            wgt::PollType::Poll => None,
846        };
847
848        // Wait for the submission index if requested.
849        if let Some(target_submission_index) = wait_submission_index {
850            log::trace!("Device::maintain: waiting for submission index {target_submission_index}");
851
852            let wait_timeout = match poll_type {
853                wgt::PollType::Wait { timeout, .. } => timeout,
854                wgt::PollType::Poll => unreachable!(
855                    "`wait_submission_index` index for poll type `Poll` should be None"
856                ),
857            };
858
859            let wait_result = unsafe {
860                self.raw()
861                    .wait(fence.as_ref(), target_submission_index, wait_timeout)
862            };
863
864            // This error match is only about `DeviceErrors`. At this stage we do not care if
865            // the wait succeeded or not, and the `Ok(bool)`` variant is ignored.
866            if let Err(e) = wait_result {
867                let hal_error: WaitIdleError = self.handle_hal_error(e).into();
868                return (user_closures, Err(hal_error));
869            }
870        }
871
872        // Get the currently finished submission index. This may be higher than the requested
873        // wait, or it may be less than the requested wait if the wait failed.
874        let fence_value_result = unsafe { self.raw().get_fence_value(fence.as_ref()) };
875        let current_finished_submission = match fence_value_result {
876            Ok(fence_value) => fence_value,
877            Err(e) => {
878                let hal_error: WaitIdleError = self.handle_hal_error(e).into();
879                return (user_closures, Err(hal_error));
880            }
881        };
882
883        // Maintain all finished submissions on the queue, updating the relevant user closures and collecting if the queue is empty.
884        //
885        // We don't use the result of the wait here, as we want to progress forward as far as possible
886        // and the wait could have been for submissions that finished long ago.
887        let mut queue_empty = false;
888        if let Some(queue) = self.get_queue() {
889            let queue_result = queue.maintain(current_finished_submission, &snatch_guard);
890            (
891                user_closures.submissions,
892                user_closures.mappings,
893                user_closures.blas_compact_ready,
894                queue_empty,
895            ) = queue_result;
896            // DEADLOCK PREVENTION: We must drop `snatch_guard` before `queue` goes out of scope.
897            //
898            // `Queue::drop` acquires the snatch guard. If we still hold it when `queue` is dropped
899            // at the end of this block, we would deadlock. This can happen in the following scenario:
900            //
901            // - Thread A calls `Device::maintain` while Thread B holds the last strong ref to the queue.
902            // - Thread A calls `self.get_queue()`, obtaining a new strong ref, and enters this branch.
903            // - Thread B drops its strong ref, making Thread A's ref the last one.
904            // - When `queue` goes out of scope here, `Queue::drop` runs and tries to acquire the
905            //   snatch guard — but Thread A (this thread) still holds it, causing a deadlock.
906            drop(snatch_guard);
907        } else {
908            drop(snatch_guard);
909        };
910
911        // Based on the queue empty status, and the current finished submission index, determine the result of the poll.
912        let result = if queue_empty {
913            if let Some(wait_submission_index) = wait_submission_index {
914                // Assert to ensure that if we received a queue empty status, the fence shows the correct value.
915                // This is defensive, as this should never be hit.
916                assert!(
917                    current_finished_submission >= wait_submission_index,
918                    "If the queue is empty, the current submission index ({current_finished_submission}) should be at least the wait submission index ({wait_submission_index})"
919                );
920            }
921
922            Ok(wgt::PollStatus::QueueEmpty)
923        } else if let Some(wait_submission_index) = wait_submission_index {
924            // This is theoretically possible to succeed more than checking on the poll result
925            // as submissions could have finished in the time between the timeout resolving,
926            // the thread getting scheduled again, and us checking the fence value.
927            if current_finished_submission >= wait_submission_index {
928                Ok(wgt::PollStatus::WaitSucceeded)
929            } else {
930                Err(WaitIdleError::Timeout)
931            }
932        } else {
933            Ok(wgt::PollStatus::Poll)
934        };
935
936        // Detect if we have been destroyed and now need to lose the device.
937        //
938        // If we are invalid (set at start of destroy) and our queue is empty,
939        // and we have a DeviceLostClosure, return the closure to be called by
940        // our caller. This will complete the steps for both destroy and for
941        // "lose the device".
942        let mut should_release_gpu_resource = false;
943        if !self.is_valid() && queue_empty {
944            // We can release gpu resources associated with this device (but not
945            // while holding the life_tracker lock).
946            should_release_gpu_resource = true;
947
948            // If we have a DeviceLostClosure, build an invocation with the
949            // reason DeviceLostReason::Destroyed and no message.
950            if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
951                user_closures
952                    .device_lost_invocations
953                    .push(DeviceLostInvocation {
954                        closure: device_lost_closure,
955                        reason: DeviceLostReason::Destroyed,
956                        message: String::new(),
957                    });
958            }
959        }
960
961        // Don't hold the locks while calling release_gpu_resources.
962        drop(fence);
963
964        if should_release_gpu_resource {
965            self.release_gpu_resources();
966        }
967
968        (user_closures, result)
969    }
970
971    pub fn create_buffer(
972        self: &Arc<Self>,
973        desc: &resource::BufferDescriptor,
974    ) -> Result<Arc<Buffer>, resource::CreateBufferError> {
975        self.check_is_valid()?;
976
977        if desc.size > self.limits.max_buffer_size {
978            return Err(resource::CreateBufferError::MaxBufferSize {
979                requested: desc.size,
980                maximum: self.limits.max_buffer_size,
981            });
982        }
983
984        if desc
985            .usage
986            .intersects(wgt::BufferUsages::BLAS_INPUT | wgt::BufferUsages::TLAS_INPUT)
987        {
988            self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)?;
989        }
990
991        if desc.usage.contains(wgt::BufferUsages::INDEX)
992            && desc.usage.contains(
993                wgt::BufferUsages::VERTEX
994                    | wgt::BufferUsages::UNIFORM
995                    | wgt::BufferUsages::INDIRECT
996                    | wgt::BufferUsages::STORAGE,
997            )
998        {
999            self.require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)?;
1000        }
1001
1002        if desc.usage.is_empty() || desc.usage.contains_unknown_bits() {
1003            return Err(resource::CreateBufferError::InvalidUsage(desc.usage));
1004        }
1005
1006        if !self
1007            .features
1008            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1009        {
1010            use wgt::BufferUsages as Bu;
1011            let write_mismatch = desc.usage.contains(Bu::MAP_WRITE)
1012                && !(Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage);
1013            let read_mismatch = desc.usage.contains(Bu::MAP_READ)
1014                && !(Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage);
1015            if write_mismatch || read_mismatch {
1016                return Err(resource::CreateBufferError::UsageMismatch(desc.usage));
1017            }
1018        }
1019
1020        let mut usage = conv::map_buffer_usage(desc.usage);
1021
1022        if desc.usage.contains(wgt::BufferUsages::INDIRECT) {
1023            self.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
1024            // We are going to be reading from it, internally;
1025            // when validating the content of the buffer
1026            usage |= wgt::BufferUses::STORAGE_READ_ONLY | wgt::BufferUses::STORAGE_READ_WRITE;
1027        }
1028
1029        if desc.usage.contains(wgt::BufferUsages::QUERY_RESOLVE) {
1030            usage |= TIMESTAMP_NORMALIZATION_BUFFER_USES;
1031        }
1032
1033        if desc.mapped_at_creation {
1034            if !desc.size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1035                return Err(resource::CreateBufferError::UnalignedSize);
1036            }
1037            if !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
1038                // we are going to be copying into it, internally
1039                usage |= wgt::BufferUses::COPY_DST;
1040            }
1041        } else {
1042            // We are required to zero out (initialize) all memory. This is done
1043            // on demand using clear_buffer which requires write transfer usage!
1044            usage |= wgt::BufferUses::COPY_DST;
1045        }
1046
1047        let actual_size = if desc.size == 0 {
1048            wgt::COPY_BUFFER_ALIGNMENT
1049        } else if desc.usage.contains(wgt::BufferUsages::VERTEX) {
1050            // Bumping the size by 1 so that we can bind an empty range at the
1051            // end of the buffer.
1052            desc.size + 1
1053        } else {
1054            desc.size
1055        };
1056        let clear_remainder = actual_size % wgt::COPY_BUFFER_ALIGNMENT;
1057        let aligned_size = if clear_remainder != 0 {
1058            actual_size + wgt::COPY_BUFFER_ALIGNMENT - clear_remainder
1059        } else {
1060            actual_size
1061        };
1062
1063        let hal_desc = hal::BufferDescriptor {
1064            label: desc.label.to_hal(self.instance_flags),
1065            size: aligned_size,
1066            usage,
1067            memory_flags: hal::MemoryFlags::empty(),
1068        };
1069        let buffer = unsafe { self.raw().create_buffer(&hal_desc) }
1070            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
1071
1072        let timestamp_normalization_bind_group = Snatchable::new(unsafe {
1073            // SAFETY: The size passed here must not overflow the buffer.
1074            self.timestamp_normalizer
1075                .get()
1076                .unwrap()
1077                .create_normalization_bind_group(
1078                    self,
1079                    &*buffer,
1080                    desc.label.as_deref(),
1081                    wgt::BufferSize::new(hal_desc.size).unwrap(),
1082                    desc.usage,
1083                )
1084        }?);
1085
1086        let indirect_validation_bind_groups =
1087            self.create_indirect_validation_bind_groups(buffer.as_ref(), desc.size, desc.usage)?;
1088
1089        let buffer = Buffer {
1090            raw: Snatchable::new(buffer),
1091            device: self.clone(),
1092            usage: desc.usage,
1093            size: desc.size,
1094            initialization_status: RwLock::new(
1095                rank::BUFFER_INITIALIZATION_STATUS,
1096                BufferInitTracker::new(aligned_size),
1097            ),
1098            map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
1099            label: desc.label.to_string(),
1100            tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),
1101            bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
1102            timestamp_normalization_bind_group,
1103            indirect_validation_bind_groups,
1104        };
1105
1106        let buffer = Arc::new(buffer);
1107
1108        let buffer_use = if !desc.mapped_at_creation {
1109            wgt::BufferUses::empty()
1110        } else if desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
1111            // buffer is mappable, so we are just doing that at start
1112            let map_size = buffer.size;
1113            let mapping = if map_size == 0 {
1114                hal::BufferMapping {
1115                    ptr: core::ptr::NonNull::dangling(),
1116                    is_coherent: true,
1117                }
1118            } else {
1119                let snatch_guard: SnatchGuard = self.snatchable_lock.read();
1120                map_buffer(&buffer, 0, map_size, HostMap::Write, &snatch_guard)?
1121            };
1122            *buffer.map_state.lock() = resource::BufferMapState::Active {
1123                mapping,
1124                range: 0..map_size,
1125                host: HostMap::Write,
1126            };
1127            wgt::BufferUses::MAP_WRITE
1128        } else {
1129            let mut staging_buffer =
1130                StagingBuffer::new(self, wgt::BufferSize::new(aligned_size).unwrap())?;
1131
1132            // Zero initialize memory and then mark the buffer as initialized
1133            // (it's guaranteed that this is the case by the time the buffer is usable)
1134            staging_buffer.write_zeros();
1135            buffer.initialization_status.write().drain(0..aligned_size);
1136
1137            *buffer.map_state.lock() = resource::BufferMapState::Init { staging_buffer };
1138            wgt::BufferUses::COPY_DST
1139        };
1140
1141        self.trackers
1142            .lock()
1143            .buffers
1144            .insert_single(&buffer, buffer_use);
1145
1146        Ok(buffer)
1147    }
1148
1149    #[cfg(feature = "replay")]
1150    pub fn set_buffer_data(
1151        self: &Arc<Self>,
1152        buffer: &Arc<Buffer>,
1153        offset: wgt::BufferAddress,
1154        data: &[u8],
1155    ) -> resource::BufferAccessResult {
1156        use crate::resource::RawResourceAccess;
1157
1158        let device = &buffer.device;
1159
1160        device.check_is_valid()?;
1161        buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?;
1162
1163        let last_submission = device
1164            .get_queue()
1165            .and_then(|queue| queue.lock_life().get_buffer_latest_submission_index(buffer));
1166
1167        if let Some(last_submission) = last_submission {
1168            device.wait_for_submit(last_submission)?;
1169        }
1170
1171        let snatch_guard = device.snatchable_lock.read();
1172        let raw_buf = buffer.try_raw(&snatch_guard)?;
1173
1174        let mapping = unsafe {
1175            device
1176                .raw()
1177                .map_buffer(raw_buf, offset..offset + data.len() as u64)
1178        }
1179        .map_err(|e| device.handle_hal_error(e))?;
1180
1181        unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()) };
1182
1183        if !mapping.is_coherent {
1184            #[allow(clippy::single_range_in_vec_init)]
1185            unsafe {
1186                device
1187                    .raw()
1188                    .flush_mapped_ranges(raw_buf, &[offset..offset + data.len() as u64])
1189            };
1190        }
1191
1192        unsafe { device.raw().unmap_buffer(raw_buf) };
1193
1194        Ok(())
1195    }
1196
1197    pub(crate) fn create_texture_from_hal(
1198        self: &Arc<Self>,
1199        hal_texture: Box<dyn hal::DynTexture>,
1200        desc: &resource::TextureDescriptor,
1201    ) -> Result<Arc<Texture>, resource::CreateTextureError> {
1202        let format_features = self
1203            .describe_format_features(desc.format)
1204            .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?;
1205
1206        unsafe { self.raw().add_raw_texture(&*hal_texture) };
1207
1208        let texture = Texture::new(
1209            self,
1210            resource::TextureInner::Native { raw: hal_texture },
1211            conv::map_texture_usage(desc.usage, desc.format.into(), format_features.flags),
1212            desc,
1213            format_features,
1214            resource::TextureClearMode::None,
1215            false,
1216        );
1217
1218        let texture = Arc::new(texture);
1219
1220        self.trackers
1221            .lock()
1222            .textures
1223            .insert_single(&texture, wgt::TextureUses::UNINITIALIZED);
1224
1225        Ok(texture)
1226    }
1227
1228    /// # Safety
1229    ///
1230    /// - `hal_buffer` must have been created on this device.
1231    /// - `hal_buffer` must have been created respecting `desc` (in particular, the size).
1232    /// - `hal_buffer` must be initialized.
1233    /// - `hal_buffer` must not have zero size.
1234    pub(crate) unsafe fn create_buffer_from_hal(
1235        self: &Arc<Self>,
1236        hal_buffer: Box<dyn hal::DynBuffer>,
1237        desc: &resource::BufferDescriptor,
1238    ) -> (Fallible<Buffer>, Option<resource::CreateBufferError>) {
1239        let timestamp_normalization_bind_group = unsafe {
1240            match self
1241                .timestamp_normalizer
1242                .get()
1243                .unwrap()
1244                .create_normalization_bind_group(
1245                    self,
1246                    &*hal_buffer,
1247                    desc.label.as_deref(),
1248                    wgt::BufferSize::new(desc.size).unwrap(),
1249                    desc.usage,
1250                ) {
1251                Ok(bg) => Snatchable::new(bg),
1252                Err(e) => {
1253                    return (
1254                        Fallible::Invalid(Arc::new(desc.label.to_string())),
1255                        Some(e.into()),
1256                    )
1257                }
1258            }
1259        };
1260
1261        let indirect_validation_bind_groups = match self.create_indirect_validation_bind_groups(
1262            hal_buffer.as_ref(),
1263            desc.size,
1264            desc.usage,
1265        ) {
1266            Ok(ok) => ok,
1267            Err(e) => return (Fallible::Invalid(Arc::new(desc.label.to_string())), Some(e)),
1268        };
1269
1270        unsafe { self.raw().add_raw_buffer(&*hal_buffer) };
1271
1272        let buffer = Buffer {
1273            raw: Snatchable::new(hal_buffer),
1274            device: self.clone(),
1275            usage: desc.usage,
1276            size: desc.size,
1277            initialization_status: RwLock::new(
1278                rank::BUFFER_INITIALIZATION_STATUS,
1279                BufferInitTracker::new(0),
1280            ),
1281            map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
1282            label: desc.label.to_string(),
1283            tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),
1284            bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
1285            timestamp_normalization_bind_group,
1286            indirect_validation_bind_groups,
1287        };
1288
1289        let buffer = Arc::new(buffer);
1290
1291        self.trackers
1292            .lock()
1293            .buffers
1294            .insert_single(&buffer, wgt::BufferUses::empty());
1295
1296        (Fallible::Valid(buffer), None)
1297    }
1298
1299    fn create_indirect_validation_bind_groups(
1300        &self,
1301        raw_buffer: &dyn hal::DynBuffer,
1302        buffer_size: u64,
1303        usage: wgt::BufferUsages,
1304    ) -> Result<Snatchable<crate::indirect_validation::BindGroups>, resource::CreateBufferError>
1305    {
1306        if !usage.contains(wgt::BufferUsages::INDIRECT) {
1307            return Ok(Snatchable::empty());
1308        }
1309
1310        let Some(ref indirect_validation) = self.indirect_validation else {
1311            return Ok(Snatchable::empty());
1312        };
1313
1314        let bind_groups = crate::indirect_validation::BindGroups::new(
1315            indirect_validation,
1316            self,
1317            buffer_size,
1318            raw_buffer,
1319        )
1320        .map_err(resource::CreateBufferError::IndirectValidationBindGroup)?;
1321
1322        if let Some(bind_groups) = bind_groups {
1323            Ok(Snatchable::new(bind_groups))
1324        } else {
1325            Ok(Snatchable::empty())
1326        }
1327    }
1328
1329    pub fn create_texture(
1330        self: &Arc<Self>,
1331        desc: &resource::TextureDescriptor,
1332    ) -> Result<Arc<Texture>, resource::CreateTextureError> {
1333        use resource::{CreateTextureError, TextureDimensionError};
1334
1335        self.check_is_valid()?;
1336
1337        if desc.usage.is_empty() || desc.usage.contains_unknown_bits() {
1338            return Err(CreateTextureError::InvalidUsage(desc.usage));
1339        }
1340
1341        conv::check_texture_dimension_size(
1342            desc.dimension,
1343            desc.size,
1344            desc.sample_count,
1345            &self.limits,
1346        )?;
1347
1348        if desc.dimension != wgt::TextureDimension::D2 {
1349            // Depth textures can only be 2D
1350            if desc.format.is_depth_stencil_format() {
1351                return Err(CreateTextureError::InvalidDepthDimension(
1352                    desc.dimension,
1353                    desc.format,
1354                ));
1355            }
1356        }
1357
1358        if desc.dimension != wgt::TextureDimension::D2
1359            && desc.dimension != wgt::TextureDimension::D3
1360        {
1361            // Compressed textures can only be 2D or 3D
1362            if desc.format.is_compressed() {
1363                return Err(CreateTextureError::InvalidCompressedDimension(
1364                    desc.dimension,
1365                    desc.format,
1366                ));
1367            }
1368
1369            // Renderable textures can only be 2D or 3D
1370            if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1371                return Err(CreateTextureError::InvalidDimensionUsages(
1372                    wgt::TextureUsages::RENDER_ATTACHMENT,
1373                    desc.dimension,
1374                ));
1375            }
1376        }
1377
1378        if desc.format.is_compressed() {
1379            let (block_width, block_height) = desc.format.block_dimensions();
1380
1381            if !desc.size.width.is_multiple_of(block_width) {
1382                return Err(CreateTextureError::InvalidDimension(
1383                    TextureDimensionError::NotMultipleOfBlockWidth {
1384                        width: desc.size.width,
1385                        block_width,
1386                        format: desc.format,
1387                    },
1388                ));
1389            }
1390
1391            if !desc.size.height.is_multiple_of(block_height) {
1392                return Err(CreateTextureError::InvalidDimension(
1393                    TextureDimensionError::NotMultipleOfBlockHeight {
1394                        height: desc.size.height,
1395                        block_height,
1396                        format: desc.format,
1397                    },
1398                ));
1399            }
1400
1401            if desc.dimension == wgt::TextureDimension::D3 {
1402                // Only BCn formats with Sliced 3D feature can be used for 3D textures
1403                if desc.format.is_bcn() {
1404                    self.require_features(wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D)
1405                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1406                } else if desc.format.is_astc() {
1407                    self.require_features(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D)
1408                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1409                } else {
1410                    return Err(CreateTextureError::InvalidCompressedDimension(
1411                        desc.dimension,
1412                        desc.format,
1413                    ));
1414                }
1415            }
1416        }
1417
1418        let mips = desc.mip_level_count;
1419        let max_levels_allowed = desc.size.max_mips(desc.dimension).min(hal::MAX_MIP_LEVELS);
1420        if mips == 0 || mips > max_levels_allowed {
1421            return Err(CreateTextureError::InvalidMipLevelCount {
1422                requested: mips,
1423                maximum: max_levels_allowed,
1424            });
1425        }
1426
1427        {
1428            let (mut width_multiple, mut height_multiple) = desc.format.size_multiple_requirement();
1429
1430            if desc.format.is_multi_planar_format() {
1431                // TODO(https://github.com/gfx-rs/wgpu/issues/8491): fix
1432                // `mip_level_size` calculation for these formats and relax this
1433                // restriction.
1434                width_multiple <<= desc.mip_level_count.saturating_sub(1);
1435                height_multiple <<= desc.mip_level_count.saturating_sub(1);
1436            }
1437
1438            if !desc.size.width.is_multiple_of(width_multiple) {
1439                return Err(CreateTextureError::InvalidDimension(
1440                    TextureDimensionError::WidthNotMultipleOf {
1441                        width: desc.size.width,
1442                        multiple: width_multiple,
1443                        format: desc.format,
1444                    },
1445                ));
1446            }
1447
1448            if !desc.size.height.is_multiple_of(height_multiple) {
1449                return Err(CreateTextureError::InvalidDimension(
1450                    TextureDimensionError::HeightNotMultipleOf {
1451                        height: desc.size.height,
1452                        multiple: height_multiple,
1453                        format: desc.format,
1454                    },
1455                ));
1456            }
1457        }
1458
1459        if desc.usage.contains(wgt::TextureUsages::TRANSIENT) {
1460            if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1461                return Err(CreateTextureError::InvalidUsage(
1462                    wgt::TextureUsages::TRANSIENT,
1463                ));
1464            }
1465            let extra_usage =
1466                desc.usage - wgt::TextureUsages::TRANSIENT - wgt::TextureUsages::RENDER_ATTACHMENT;
1467            if !extra_usage.is_empty() {
1468                return Err(CreateTextureError::IncompatibleUsage(
1469                    wgt::TextureUsages::TRANSIENT,
1470                    extra_usage,
1471                ));
1472            }
1473        }
1474
1475        let format_features = self
1476            .describe_format_features(desc.format)
1477            .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1478
1479        if desc.sample_count > 1 {
1480            // <https://www.w3.org/TR/2025/CRD-webgpu-20251120/#:~:text=If%20descriptor%2EsampleCount%20%3E%201>
1481            //
1482            // Note that there are also some checks related to the sample count
1483            // in [`conv::check_texture_dimension_size`].
1484
1485            if desc.mip_level_count != 1 {
1486                return Err(CreateTextureError::InvalidMipLevelCount {
1487                    requested: desc.mip_level_count,
1488                    maximum: 1,
1489                });
1490            }
1491
1492            if desc.size.depth_or_array_layers != 1
1493                && !self.features.contains(wgt::Features::MULTISAMPLE_ARRAY)
1494            {
1495                return Err(CreateTextureError::InvalidDimension(
1496                    TextureDimensionError::MultisampledDepthOrArrayLayer(
1497                        desc.size.depth_or_array_layers,
1498                    ),
1499                ));
1500            }
1501
1502            if desc.usage.contains(wgt::TextureUsages::STORAGE_BINDING) {
1503                return Err(CreateTextureError::InvalidMultisampledStorageBinding);
1504            }
1505
1506            if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1507                return Err(CreateTextureError::MultisampledNotRenderAttachment);
1508            }
1509
1510            if !format_features.flags.intersects(
1511                wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4
1512                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2
1513                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8
1514                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1515            ) {
1516                return Err(CreateTextureError::InvalidMultisampledFormat(desc.format));
1517            }
1518
1519            if !format_features
1520                .flags
1521                .sample_count_supported(desc.sample_count)
1522            {
1523                return Err(CreateTextureError::InvalidSampleCount(
1524                    desc.sample_count,
1525                    desc.format,
1526                    desc.format
1527                        .guaranteed_format_features(self.features)
1528                        .flags
1529                        .supported_sample_counts(),
1530                    self.adapter
1531                        .get_texture_format_features(desc.format)
1532                        .flags
1533                        .supported_sample_counts(),
1534                ));
1535            };
1536        }
1537
1538        let missing_allowed_usages = match desc.format.planes() {
1539            Some(planes) => {
1540                let mut planes_usages = wgt::TextureUsages::all();
1541                for plane in 0..planes {
1542                    let aspect = wgt::TextureAspect::from_plane(plane).unwrap();
1543                    let format = desc.format.aspect_specific_format(aspect).unwrap();
1544                    let format_features = self
1545                        .describe_format_features(format)
1546                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1547
1548                    planes_usages &= format_features.allowed_usages;
1549                }
1550
1551                desc.usage - planes_usages
1552            }
1553            None => desc.usage - format_features.allowed_usages,
1554        };
1555
1556        if !missing_allowed_usages.is_empty() {
1557            // detect downlevel incompatibilities
1558            let wgpu_allowed_usages = desc
1559                .format
1560                .guaranteed_format_features(self.features)
1561                .allowed_usages;
1562            let wgpu_missing_usages = desc.usage - wgpu_allowed_usages;
1563            return Err(CreateTextureError::InvalidFormatUsages(
1564                missing_allowed_usages,
1565                desc.format,
1566                wgpu_missing_usages.is_empty(),
1567            ));
1568        }
1569
1570        let mut hal_view_formats = Vec::new();
1571        for format in desc.view_formats.iter() {
1572            if desc.format == *format {
1573                continue;
1574            }
1575            if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
1576                return Err(CreateTextureError::InvalidViewFormat(*format, desc.format));
1577            }
1578            hal_view_formats.push(*format);
1579        }
1580        if !hal_view_formats.is_empty() {
1581            self.require_downlevel_flags(wgt::DownlevelFlags::VIEW_FORMATS)?;
1582        }
1583
1584        let hal_usage = conv::map_texture_usage_for_texture(desc, &format_features);
1585
1586        let hal_desc = hal::TextureDescriptor {
1587            label: desc.label.to_hal(self.instance_flags),
1588            size: desc.size,
1589            mip_level_count: desc.mip_level_count,
1590            sample_count: desc.sample_count,
1591            dimension: desc.dimension,
1592            format: desc.format,
1593            usage: hal_usage,
1594            memory_flags: hal::MemoryFlags::empty(),
1595            view_formats: hal_view_formats,
1596        };
1597
1598        let raw_texture = unsafe { self.raw().create_texture(&hal_desc) }
1599            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
1600
1601        let clear_mode = if hal_usage
1602            .intersects(wgt::TextureUses::DEPTH_STENCIL_WRITE | wgt::TextureUses::COLOR_TARGET)
1603            && desc.dimension == wgt::TextureDimension::D2
1604        {
1605            let (is_color, usage) = if desc.format.is_depth_stencil_format() {
1606                (false, wgt::TextureUses::DEPTH_STENCIL_WRITE)
1607            } else {
1608                (true, wgt::TextureUses::COLOR_TARGET)
1609            };
1610
1611            let clear_label = hal_label(
1612                Some("(wgpu internal) clear texture view"),
1613                self.instance_flags,
1614            );
1615
1616            let mut clear_views = SmallVec::new();
1617            for mip_level in 0..desc.mip_level_count {
1618                for array_layer in 0..desc.size.depth_or_array_layers {
1619                    macro_rules! push_clear_view {
1620                        ($format:expr, $aspect:expr) => {
1621                            let desc = hal::TextureViewDescriptor {
1622                                label: clear_label,
1623                                format: $format,
1624                                dimension: TextureViewDimension::D2,
1625                                usage,
1626                                range: wgt::ImageSubresourceRange {
1627                                    aspect: $aspect,
1628                                    base_mip_level: mip_level,
1629                                    mip_level_count: Some(1),
1630                                    base_array_layer: array_layer,
1631                                    array_layer_count: Some(1),
1632                                },
1633                            };
1634                            clear_views.push(ManuallyDrop::new(
1635                                unsafe {
1636                                    self.raw().create_texture_view(raw_texture.as_ref(), &desc)
1637                                }
1638                                .map_err(|e| self.handle_hal_error(e))?,
1639                            ));
1640                        };
1641                    }
1642
1643                    if let Some(planes) = desc.format.planes() {
1644                        for plane in 0..planes {
1645                            let aspect = wgt::TextureAspect::from_plane(plane).unwrap();
1646                            let format = desc.format.aspect_specific_format(aspect).unwrap();
1647                            push_clear_view!(format, aspect);
1648                        }
1649                    } else {
1650                        push_clear_view!(desc.format, wgt::TextureAspect::All);
1651                    }
1652                }
1653            }
1654            resource::TextureClearMode::RenderPass {
1655                clear_views,
1656                is_color,
1657            }
1658        } else {
1659            resource::TextureClearMode::BufferCopy
1660        };
1661
1662        let texture = Texture::new(
1663            self,
1664            resource::TextureInner::Native { raw: raw_texture },
1665            hal_usage,
1666            desc,
1667            format_features,
1668            clear_mode,
1669            true,
1670        );
1671
1672        let texture = Arc::new(texture);
1673
1674        self.trackers
1675            .lock()
1676            .textures
1677            .insert_single(&texture, wgt::TextureUses::UNINITIALIZED);
1678
1679        Ok(texture)
1680    }
1681
1682    pub fn create_texture_view(
1683        self: &Arc<Self>,
1684        texture: &Arc<Texture>,
1685        desc: &resource::TextureViewDescriptor,
1686    ) -> Result<Arc<TextureView>, resource::CreateTextureViewError> {
1687        self.check_is_valid()?;
1688
1689        let snatch_guard = texture.device.snatchable_lock.read();
1690
1691        let texture_raw = texture.try_raw(&snatch_guard)?;
1692
1693        // resolve TextureViewDescriptor defaults
1694        // https://gpuweb.github.io/gpuweb/#abstract-opdef-resolving-gputextureviewdescriptor-defaults
1695        let resolved_format = desc.format.unwrap_or_else(|| {
1696            texture
1697                .desc
1698                .format
1699                .aspect_specific_format(desc.range.aspect)
1700                .unwrap_or(texture.desc.format)
1701        });
1702
1703        let resolved_dimension = desc
1704            .dimension
1705            .unwrap_or_else(|| match texture.desc.dimension {
1706                wgt::TextureDimension::D1 => TextureViewDimension::D1,
1707                wgt::TextureDimension::D2 => {
1708                    if texture.desc.array_layer_count() == 1 {
1709                        TextureViewDimension::D2
1710                    } else {
1711                        TextureViewDimension::D2Array
1712                    }
1713                }
1714                wgt::TextureDimension::D3 => TextureViewDimension::D3,
1715            });
1716
1717        let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {
1718            texture
1719                .desc
1720                .mip_level_count
1721                .saturating_sub(desc.range.base_mip_level)
1722        });
1723
1724        let resolved_array_layer_count =
1725            desc.range
1726                .array_layer_count
1727                .unwrap_or_else(|| match resolved_dimension {
1728                    TextureViewDimension::D1
1729                    | TextureViewDimension::D2
1730                    | TextureViewDimension::D3 => 1,
1731                    TextureViewDimension::Cube => 6,
1732                    TextureViewDimension::D2Array | TextureViewDimension::CubeArray => texture
1733                        .desc
1734                        .array_layer_count()
1735                        .saturating_sub(desc.range.base_array_layer),
1736                });
1737
1738        let resolved_usage = {
1739            let usage = desc.usage.unwrap_or(wgt::TextureUsages::empty());
1740            if usage.is_empty() {
1741                texture.desc.usage
1742            } else if texture.desc.usage.contains(usage) {
1743                usage
1744            } else {
1745                return Err(resource::CreateTextureViewError::InvalidTextureViewUsage {
1746                    view: usage,
1747                    texture: texture.desc.usage,
1748                });
1749            }
1750        };
1751
1752        let format_features = self.describe_format_features(resolved_format)?;
1753        let allowed_format_usages = format_features.allowed_usages;
1754        if resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1755            && !allowed_format_usages.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1756        {
1757            return Err(
1758                resource::CreateTextureViewError::TextureViewFormatNotRenderable(resolved_format),
1759            );
1760        }
1761
1762        if resolved_usage.contains(wgt::TextureUsages::STORAGE_BINDING)
1763            && !allowed_format_usages.contains(wgt::TextureUsages::STORAGE_BINDING)
1764        {
1765            return Err(
1766                resource::CreateTextureViewError::TextureViewFormatNotStorage(resolved_format),
1767            );
1768        }
1769
1770        // validate TextureViewDescriptor
1771
1772        let aspects = hal::FormatAspects::new(texture.desc.format, desc.range.aspect);
1773        if aspects.is_empty() {
1774            return Err(resource::CreateTextureViewError::InvalidAspect {
1775                texture_format: texture.desc.format,
1776                requested_aspect: desc.range.aspect,
1777            });
1778        }
1779
1780        let format_is_good = if desc.range.aspect == wgt::TextureAspect::All {
1781            resolved_format == texture.desc.format
1782                || texture.desc.view_formats.contains(&resolved_format)
1783        } else {
1784            Some(resolved_format)
1785                == texture
1786                    .desc
1787                    .format
1788                    .aspect_specific_format(desc.range.aspect)
1789        };
1790        if !format_is_good {
1791            return Err(resource::CreateTextureViewError::FormatReinterpretation {
1792                texture: texture.desc.format,
1793                view: resolved_format,
1794            });
1795        }
1796
1797        // check if multisampled texture is seen as anything but 2D
1798        if texture.desc.sample_count > 1 && resolved_dimension != TextureViewDimension::D2 {
1799            // Multisample is allowed on 2D arrays, only if explicitly supported
1800            let multisample_array_exception = resolved_dimension == TextureViewDimension::D2Array
1801                && self.features.contains(wgt::Features::MULTISAMPLE_ARRAY);
1802
1803            if !multisample_array_exception {
1804                return Err(
1805                    resource::CreateTextureViewError::InvalidMultisampledTextureViewDimension(
1806                        resolved_dimension,
1807                    ),
1808                );
1809            }
1810        }
1811
1812        // check if the dimension is compatible with the texture
1813        if texture.desc.dimension != resolved_dimension.compatible_texture_dimension() {
1814            return Err(
1815                resource::CreateTextureViewError::InvalidTextureViewDimension {
1816                    view: resolved_dimension,
1817                    texture: texture.desc.dimension,
1818                },
1819            );
1820        }
1821
1822        match resolved_dimension {
1823            TextureViewDimension::D1 | TextureViewDimension::D2 | TextureViewDimension::D3 => {
1824                if resolved_array_layer_count != 1 {
1825                    return Err(resource::CreateTextureViewError::InvalidArrayLayerCount {
1826                        requested: resolved_array_layer_count,
1827                        dim: resolved_dimension,
1828                    });
1829                }
1830            }
1831            TextureViewDimension::Cube => {
1832                if resolved_array_layer_count != 6 {
1833                    return Err(
1834                        resource::CreateTextureViewError::InvalidCubemapTextureDepth {
1835                            depth: resolved_array_layer_count,
1836                        },
1837                    );
1838                }
1839            }
1840            TextureViewDimension::CubeArray => {
1841                if !resolved_array_layer_count.is_multiple_of(6) {
1842                    return Err(
1843                        resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth {
1844                            depth: resolved_array_layer_count,
1845                        },
1846                    );
1847                }
1848            }
1849            _ => {}
1850        }
1851
1852        match resolved_dimension {
1853            TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1854                if texture.desc.size.width != texture.desc.size.height {
1855                    return Err(resource::CreateTextureViewError::InvalidCubeTextureViewSize);
1856                }
1857            }
1858            _ => {}
1859        }
1860
1861        if resolved_mip_level_count == 0 {
1862            return Err(resource::CreateTextureViewError::ZeroMipLevelCount);
1863        }
1864
1865        let mip_level_end = desc
1866            .range
1867            .base_mip_level
1868            .saturating_add(resolved_mip_level_count);
1869
1870        let level_end = texture.desc.mip_level_count;
1871        if mip_level_end > level_end {
1872            return Err(resource::CreateTextureViewError::TooManyMipLevels {
1873                base_mip_level: desc.range.base_mip_level,
1874                mip_level_count: resolved_mip_level_count,
1875                total: level_end,
1876            });
1877        }
1878
1879        if resolved_array_layer_count == 0 {
1880            return Err(resource::CreateTextureViewError::ZeroArrayLayerCount);
1881        }
1882
1883        let array_layer_end = desc
1884            .range
1885            .base_array_layer
1886            .saturating_add(resolved_array_layer_count);
1887
1888        let layer_end = texture.desc.array_layer_count();
1889        if array_layer_end > layer_end {
1890            return Err(resource::CreateTextureViewError::TooManyArrayLayers {
1891                base_array_layer: desc.range.base_array_layer,
1892                array_layer_count: resolved_array_layer_count,
1893                total: layer_end,
1894            });
1895        };
1896
1897        // https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view
1898        let render_extent = 'error: {
1899            if !resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1900                break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage));
1901            }
1902
1903            let allowed_view_dimensions = [
1904                TextureViewDimension::D2,
1905                TextureViewDimension::D2Array,
1906                TextureViewDimension::D3,
1907            ];
1908            if !allowed_view_dimensions.contains(&resolved_dimension) {
1909                break 'error Err(TextureViewNotRenderableReason::Dimension(
1910                    resolved_dimension,
1911                ));
1912            }
1913
1914            if resolved_mip_level_count != 1 {
1915                break 'error Err(TextureViewNotRenderableReason::MipLevelCount(
1916                    resolved_mip_level_count,
1917                ));
1918            }
1919
1920            if resolved_array_layer_count != 1
1921                && !(self.features.contains(wgt::Features::MULTIVIEW))
1922            {
1923                break 'error Err(TextureViewNotRenderableReason::ArrayLayerCount(
1924                    resolved_array_layer_count,
1925                ));
1926            }
1927
1928            if !texture.desc.format.is_multi_planar_format()
1929                && aspects != hal::FormatAspects::from(texture.desc.format)
1930            {
1931                break 'error Err(TextureViewNotRenderableReason::Aspects(aspects));
1932            }
1933
1934            Ok(texture
1935                .desc
1936                .compute_render_extent(desc.range.base_mip_level, desc.range.aspect.to_plane()))
1937        };
1938
1939        // filter the usages based on the other criteria
1940        let usage = {
1941            let resolved_hal_usage = conv::map_texture_usage(
1942                resolved_usage,
1943                resolved_format.into(),
1944                format_features.flags,
1945            );
1946            let mask_copy = !(wgt::TextureUses::COPY_SRC | wgt::TextureUses::COPY_DST);
1947            let mask_dimension = match resolved_dimension {
1948                TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1949                    wgt::TextureUses::RESOURCE
1950                }
1951                TextureViewDimension::D3 => {
1952                    wgt::TextureUses::RESOURCE
1953                        | wgt::TextureUses::STORAGE_READ_ONLY
1954                        | wgt::TextureUses::STORAGE_WRITE_ONLY
1955                        | wgt::TextureUses::STORAGE_READ_WRITE
1956                }
1957                _ => wgt::TextureUses::all(),
1958            };
1959            let mask_mip_level = if resolved_mip_level_count == 1 {
1960                wgt::TextureUses::all()
1961            } else {
1962                wgt::TextureUses::RESOURCE
1963            };
1964            resolved_hal_usage & mask_copy & mask_dimension & mask_mip_level
1965        };
1966
1967        // use the combined depth-stencil format for the view
1968        let format = if resolved_format.is_depth_stencil_component(texture.desc.format) {
1969            texture.desc.format
1970        } else {
1971            resolved_format
1972        };
1973
1974        let resolved_range = wgt::ImageSubresourceRange {
1975            aspect: desc.range.aspect,
1976            base_mip_level: desc.range.base_mip_level,
1977            mip_level_count: Some(resolved_mip_level_count),
1978            base_array_layer: desc.range.base_array_layer,
1979            array_layer_count: Some(resolved_array_layer_count),
1980        };
1981
1982        let hal_desc = hal::TextureViewDescriptor {
1983            label: desc.label.to_hal(self.instance_flags),
1984            format,
1985            dimension: resolved_dimension,
1986            usage,
1987            range: resolved_range,
1988        };
1989
1990        let raw = unsafe { self.raw().create_texture_view(texture_raw, &hal_desc) }
1991            .map_err(|e| self.handle_hal_error(e))?;
1992
1993        let selector = TextureSelector {
1994            mips: desc.range.base_mip_level..mip_level_end,
1995            layers: desc.range.base_array_layer..array_layer_end,
1996        };
1997
1998        let view = TextureView {
1999            raw: Snatchable::new(raw),
2000            parent: texture.clone(),
2001            device: self.clone(),
2002            desc: resource::HalTextureViewDescriptor {
2003                texture_format: texture.desc.format,
2004                format: resolved_format,
2005                dimension: resolved_dimension,
2006                usage: resolved_usage,
2007                range: resolved_range,
2008            },
2009            format_features: texture.format_features,
2010            render_extent,
2011            samples: texture.desc.sample_count,
2012            selector,
2013            label: desc.label.to_string(),
2014        };
2015
2016        let view = Arc::new(view);
2017
2018        {
2019            let mut views = texture.views.lock();
2020            views.push(Arc::downgrade(&view));
2021        }
2022
2023        Ok(view)
2024    }
2025
2026    pub fn create_external_texture(
2027        self: &Arc<Self>,
2028        desc: &resource::ExternalTextureDescriptor,
2029        planes: &[Arc<TextureView>],
2030    ) -> Result<Arc<ExternalTexture>, resource::CreateExternalTextureError> {
2031        use resource::CreateExternalTextureError;
2032        self.require_features(wgt::Features::EXTERNAL_TEXTURE)?;
2033        self.check_is_valid()?;
2034
2035        if desc.num_planes() != planes.len() {
2036            return Err(CreateExternalTextureError::IncorrectPlaneCount {
2037                format: desc.format,
2038                expected: desc.num_planes(),
2039                provided: planes.len(),
2040            });
2041        }
2042
2043        let planes = planes
2044            .iter()
2045            .enumerate()
2046            .map(|(i, plane)| {
2047                if plane.samples != 1 {
2048                    return Err(CreateExternalTextureError::InvalidPlaneMultisample(
2049                        plane.samples,
2050                    ));
2051                }
2052
2053                let sample_type = plane
2054                    .desc
2055                    .format
2056                    .sample_type(Some(plane.desc.range.aspect), Some(self.features))
2057                    .unwrap();
2058                if !matches!(sample_type, TextureSampleType::Float { filterable: true }) {
2059                    return Err(CreateExternalTextureError::InvalidPlaneSampleType {
2060                        format: plane.desc.format,
2061                        sample_type,
2062                    });
2063                }
2064
2065                if plane.desc.dimension != TextureViewDimension::D2 {
2066                    return Err(CreateExternalTextureError::InvalidPlaneDimension(
2067                        plane.desc.dimension,
2068                    ));
2069                }
2070
2071                let expected_components = match desc.format {
2072                    wgt::ExternalTextureFormat::Rgba => 4,
2073                    wgt::ExternalTextureFormat::Nv12 => match i {
2074                        0 => 1,
2075                        1 => 2,
2076                        _ => unreachable!(),
2077                    },
2078                    wgt::ExternalTextureFormat::Yu12 => 1,
2079                };
2080                if plane.desc.format.components() != expected_components {
2081                    return Err(CreateExternalTextureError::InvalidPlaneFormat {
2082                        format: desc.format,
2083                        plane: i,
2084                        expected: expected_components,
2085                        provided: plane.desc.format,
2086                    });
2087                }
2088
2089                plane.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
2090                Ok(plane.clone())
2091            })
2092            .collect::<Result<_, _>>()?;
2093
2094        let params_data = ExternalTextureParams::from_desc(desc);
2095        let label = desc.label.as_ref().map(|l| alloc::format!("{l} params"));
2096        let params_desc = resource::BufferDescriptor {
2097            label: label.map(Cow::Owned),
2098            size: size_of_val(&params_data) as wgt::BufferAddress,
2099            usage: wgt::BufferUsages::UNIFORM | wgt::BufferUsages::COPY_DST,
2100            mapped_at_creation: false,
2101        };
2102        let params = self.create_buffer(&params_desc)?;
2103        self.get_queue().unwrap().write_buffer(
2104            params.clone(),
2105            0,
2106            bytemuck::bytes_of(&params_data),
2107        )?;
2108
2109        let external_texture = ExternalTexture {
2110            device: self.clone(),
2111            planes,
2112            params,
2113            label: desc.label.to_string(),
2114            tracking_data: TrackingData::new(self.tracker_indices.external_textures.clone()),
2115        };
2116        let external_texture = Arc::new(external_texture);
2117
2118        Ok(external_texture)
2119    }
2120
2121    pub fn create_sampler(
2122        self: &Arc<Self>,
2123        desc: &resource::SamplerDescriptor,
2124    ) -> Result<Arc<Sampler>, resource::CreateSamplerError> {
2125        self.check_is_valid()?;
2126
2127        if desc
2128            .address_modes
2129            .iter()
2130            .any(|am| am == &wgt::AddressMode::ClampToBorder)
2131        {
2132            self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER)?;
2133        }
2134
2135        if desc.border_color == Some(wgt::SamplerBorderColor::Zero) {
2136            self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;
2137        }
2138
2139        if desc.lod_min_clamp < 0.0 {
2140            return Err(resource::CreateSamplerError::InvalidLodMinClamp(
2141                desc.lod_min_clamp,
2142            ));
2143        }
2144        if desc.lod_max_clamp < desc.lod_min_clamp {
2145            return Err(resource::CreateSamplerError::InvalidLodMaxClamp {
2146                lod_min_clamp: desc.lod_min_clamp,
2147                lod_max_clamp: desc.lod_max_clamp,
2148            });
2149        }
2150
2151        if desc.anisotropy_clamp < 1 {
2152            return Err(resource::CreateSamplerError::InvalidAnisotropy(
2153                desc.anisotropy_clamp,
2154            ));
2155        }
2156
2157        if desc.anisotropy_clamp != 1 {
2158            if !matches!(desc.min_filter, wgt::FilterMode::Linear) {
2159                return Err(
2160                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
2161                        filter_type: resource::SamplerFilterErrorType::MinFilter,
2162                        filter_mode: desc.min_filter,
2163                        anisotropic_clamp: desc.anisotropy_clamp,
2164                    },
2165                );
2166            }
2167            if !matches!(desc.mag_filter, wgt::FilterMode::Linear) {
2168                return Err(
2169                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
2170                        filter_type: resource::SamplerFilterErrorType::MagFilter,
2171                        filter_mode: desc.mag_filter,
2172                        anisotropic_clamp: desc.anisotropy_clamp,
2173                    },
2174                );
2175            }
2176            if !matches!(desc.mipmap_filter, wgt::MipmapFilterMode::Linear) {
2177                return Err(
2178                    resource::CreateSamplerError::InvalidMipmapFilterModeWithAnisotropy {
2179                        filter_type: resource::SamplerFilterErrorType::MipmapFilter,
2180                        filter_mode: desc.mipmap_filter,
2181                        anisotropic_clamp: desc.anisotropy_clamp,
2182                    },
2183                );
2184            }
2185        }
2186
2187        let anisotropy_clamp = if self
2188            .downlevel
2189            .flags
2190            .contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING)
2191        {
2192            // Clamp anisotropy clamp to [1, 16] per the wgpu-hal interface
2193            desc.anisotropy_clamp.min(16)
2194        } else {
2195            // If it isn't supported, set this unconditionally to 1
2196            1
2197        };
2198
2199        //TODO: check for wgt::DownlevelFlags::COMPARISON_SAMPLERS
2200
2201        let hal_desc = hal::SamplerDescriptor {
2202            label: desc.label.to_hal(self.instance_flags),
2203            address_modes: desc.address_modes,
2204            mag_filter: desc.mag_filter,
2205            min_filter: desc.min_filter,
2206            mipmap_filter: desc.mipmap_filter,
2207            lod_clamp: desc.lod_min_clamp..desc.lod_max_clamp,
2208            compare: desc.compare,
2209            anisotropy_clamp,
2210            border_color: desc.border_color,
2211        };
2212
2213        let raw = unsafe { self.raw().create_sampler(&hal_desc) }
2214            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
2215
2216        let sampler = Sampler {
2217            raw: ManuallyDrop::new(raw),
2218            device: self.clone(),
2219            label: desc.label.to_string(),
2220            tracking_data: TrackingData::new(self.tracker_indices.samplers.clone()),
2221            comparison: desc.compare.is_some(),
2222            filtering: desc.min_filter == wgt::FilterMode::Linear
2223                || desc.mag_filter == wgt::FilterMode::Linear
2224                || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,
2225        };
2226
2227        let sampler = Arc::new(sampler);
2228
2229        Ok(sampler)
2230    }
2231
2232    pub fn create_shader_module<'a>(
2233        self: &Arc<Self>,
2234        desc: &pipeline::ShaderModuleDescriptor<'a>,
2235        source: pipeline::ShaderModuleSource<'a>,
2236    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
2237        self.check_is_valid()?;
2238
2239        let (module, source) = match source {
2240            #[cfg(feature = "wgsl")]
2241            pipeline::ShaderModuleSource::Wgsl(code) => {
2242                profiling::scope!("naga::front::wgsl::parse");
2243                let capabilities =
2244                    features_to_naga_capabilities(self.features, self.downlevel.flags);
2245                let mut options = naga::front::wgsl::Options::new();
2246                options.capabilities = capabilities;
2247                let mut frontend = naga::front::wgsl::Frontend::new_with_options(options);
2248                let module = frontend.parse(&code).map_err(|inner| {
2249                    pipeline::CreateShaderModuleError::Parsing(naga::error::ShaderError {
2250                        source: code.to_string(),
2251                        label: desc.label.as_ref().map(|l| l.to_string()),
2252                        inner: Box::new(inner),
2253                    })
2254                })?;
2255                (Cow::Owned(module), code.into_owned())
2256            }
2257            #[cfg(feature = "spirv")]
2258            pipeline::ShaderModuleSource::SpirV(spv, options) => {
2259                let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options);
2260                profiling::scope!("naga::front::spv::Frontend");
2261                let module = parser.parse().map_err(|inner| {
2262                    pipeline::CreateShaderModuleError::ParsingSpirV(naga::error::ShaderError {
2263                        source: String::new(),
2264                        label: desc.label.as_ref().map(|l| l.to_string()),
2265                        inner: Box::new(inner),
2266                    })
2267                })?;
2268                (Cow::Owned(module), String::new())
2269            }
2270            #[cfg(feature = "glsl")]
2271            pipeline::ShaderModuleSource::Glsl(code, options) => {
2272                let mut parser = naga::front::glsl::Frontend::default();
2273                profiling::scope!("naga::front::glsl::Frontend.parse");
2274                let module = parser.parse(&options, &code).map_err(|inner| {
2275                    pipeline::CreateShaderModuleError::ParsingGlsl(naga::error::ShaderError {
2276                        source: code.to_string(),
2277                        label: desc.label.as_ref().map(|l| l.to_string()),
2278                        inner: Box::new(inner),
2279                    })
2280                })?;
2281                (Cow::Owned(module), code.into_owned())
2282            }
2283            pipeline::ShaderModuleSource::Naga(module) => (module, String::new()),
2284            pipeline::ShaderModuleSource::Dummy(_) => panic!("found `ShaderModuleSource::Dummy`"),
2285        };
2286        for (_, var) in module.global_variables.iter() {
2287            match var.binding {
2288                Some(br) if br.group >= self.limits.max_bind_groups => {
2289                    return Err(pipeline::CreateShaderModuleError::InvalidGroupIndex {
2290                        bind: br,
2291                        group: br.group,
2292                        limit: self.limits.max_bind_groups,
2293                    });
2294                }
2295                _ => continue,
2296            };
2297        }
2298
2299        profiling::scope!("naga::validate");
2300        let debug_source =
2301            if self.instance_flags.contains(wgt::InstanceFlags::DEBUG) && !source.is_empty() {
2302                Some(hal::DebugSource {
2303                    file_name: Cow::Owned(
2304                        desc.label
2305                            .as_ref()
2306                            .map_or("shader".to_string(), |l| l.to_string()),
2307                    ),
2308                    source_code: Cow::Owned(source.clone()),
2309                })
2310            } else {
2311                None
2312            };
2313
2314        let info = create_validator(
2315            self.features,
2316            self.downlevel.flags,
2317            naga::valid::ValidationFlags::all(),
2318        )
2319        .validate(&module)
2320        .map_err(|inner| {
2321            pipeline::CreateShaderModuleError::Validation(naga::error::ShaderError {
2322                source,
2323                label: desc.label.as_ref().map(|l| l.to_string()),
2324                inner: Box::new(inner),
2325            })
2326        })?;
2327
2328        let interface = validation::Interface::new(&module, &info, self.limits.clone());
2329        let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {
2330            module,
2331            info,
2332            debug_source,
2333        });
2334        let hal_desc = hal::ShaderModuleDescriptor {
2335            label: desc.label.to_hal(self.instance_flags),
2336            runtime_checks: desc.runtime_checks,
2337        };
2338        let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {
2339            Ok(raw) => raw,
2340            Err(error) => {
2341                return Err(match error {
2342                    hal::ShaderError::Device(error) => {
2343                        pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
2344                    }
2345                    hal::ShaderError::Compilation(ref msg) => {
2346                        log::error!("Shader error: {msg}");
2347                        pipeline::CreateShaderModuleError::Generation
2348                    }
2349                })
2350            }
2351        };
2352
2353        let module = pipeline::ShaderModule {
2354            raw: ManuallyDrop::new(raw),
2355            device: self.clone(),
2356            interface: Some(interface),
2357            label: desc.label.to_string(),
2358        };
2359
2360        let module = Arc::new(module);
2361
2362        Ok(module)
2363    }
2364
2365    /// Not a public API. For use by `player` only.
2366    #[allow(unused_unsafe)]
2367    #[doc(hidden)]
2368    pub unsafe fn create_shader_module_passthrough<'a>(
2369        self: &Arc<Self>,
2370        descriptor: &pipeline::ShaderModuleDescriptorPassthrough<'a>,
2371    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
2372        self.check_is_valid()?;
2373        self.require_features(wgt::Features::PASSTHROUGH_SHADERS)?;
2374
2375        let hal_shader = match self.backend() {
2376            wgt::Backend::Vulkan => hal::ShaderInput::SpirV(
2377                descriptor
2378                    .spirv
2379                    .as_ref()
2380                    .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,
2381            ),
2382            wgt::Backend::Dx12 => {
2383                if let Some(dxil) = &descriptor.dxil {
2384                    hal::ShaderInput::Dxil {
2385                        shader: dxil,
2386                        num_workgroups: descriptor.num_workgroups,
2387                    }
2388                } else if let Some(hlsl) = &descriptor.hlsl {
2389                    hal::ShaderInput::Hlsl {
2390                        shader: hlsl,
2391                        num_workgroups: descriptor.num_workgroups,
2392                    }
2393                } else {
2394                    return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend);
2395                }
2396            }
2397            wgt::Backend::Metal => {
2398                if let Some(metallib) = &descriptor.metallib {
2399                    hal::ShaderInput::MetalLib {
2400                        file: metallib,
2401                        num_workgroups: descriptor.num_workgroups,
2402                    }
2403                } else if let Some(msl) = &descriptor.msl {
2404                    hal::ShaderInput::Msl {
2405                        shader: msl,
2406                        num_workgroups: descriptor.num_workgroups,
2407                    }
2408                } else {
2409                    return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend);
2410                }
2411            }
2412            wgt::Backend::Gl => hal::ShaderInput::Glsl {
2413                shader: descriptor
2414                    .glsl
2415                    .as_ref()
2416                    .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,
2417                num_workgroups: descriptor.num_workgroups,
2418            },
2419            wgt::Backend::Noop => {
2420                return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend)
2421            }
2422            wgt::Backend::BrowserWebGpu => unreachable!(),
2423        };
2424
2425        let hal_desc = hal::ShaderModuleDescriptor {
2426            label: descriptor.label.to_hal(self.instance_flags),
2427            runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),
2428        };
2429
2430        let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {
2431            Ok(raw) => raw,
2432            Err(error) => {
2433                return Err(match error {
2434                    hal::ShaderError::Device(error) => {
2435                        pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
2436                    }
2437                    hal::ShaderError::Compilation(ref msg) => {
2438                        log::error!("Shader error: {msg}");
2439                        pipeline::CreateShaderModuleError::Generation
2440                    }
2441                })
2442            }
2443        };
2444
2445        let module = pipeline::ShaderModule {
2446            raw: ManuallyDrop::new(raw),
2447            device: self.clone(),
2448            interface: None,
2449            label: descriptor.label.to_string(),
2450        };
2451
2452        Ok(Arc::new(module))
2453    }
2454
2455    pub(crate) fn create_command_encoder(
2456        self: &Arc<Self>,
2457        label: &crate::Label,
2458    ) -> Result<Arc<command::CommandEncoder>, DeviceError> {
2459        self.check_is_valid()?;
2460
2461        let queue = self.get_queue().unwrap();
2462
2463        let encoder = self
2464            .command_allocator
2465            .acquire_encoder(self.raw(), queue.raw())
2466            .map_err(|e| self.handle_hal_error(e))?;
2467
2468        let cmd_enc = command::CommandEncoder::new(encoder, self, label);
2469
2470        let cmd_enc = Arc::new(cmd_enc);
2471
2472        Ok(cmd_enc)
2473    }
2474
2475    /// Generate information about late-validated buffer bindings for pipelines.
2476    //TODO: should this be combined with `get_introspection_bind_group_layouts` in some way?
2477    fn make_late_sized_buffer_groups(
2478        shader_binding_sizes: &FastHashMap<naga::ResourceBinding, wgt::BufferSize>,
2479        layout: &binding_model::PipelineLayout,
2480    ) -> ArrayVec<pipeline::LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }> {
2481        // Given the shader-required binding sizes and the pipeline layout,
2482        // return the filtered list of them in the layout order,
2483        // removing those with given `min_binding_size`.
2484        layout
2485            .bind_group_layouts
2486            .iter()
2487            .enumerate()
2488            .map(|(group_index, bgl)| {
2489                let Some(bgl) = bgl else {
2490                    return pipeline::LateSizedBufferGroup::default();
2491                };
2492
2493                let shader_sizes = bgl
2494                    .entries
2495                    .values()
2496                    .filter_map(|entry| match entry.ty {
2497                        wgt::BindingType::Buffer {
2498                            min_binding_size: None,
2499                            ..
2500                        } => {
2501                            let rb = naga::ResourceBinding {
2502                                group: group_index as u32,
2503                                binding: entry.binding,
2504                            };
2505                            let shader_size =
2506                                shader_binding_sizes.get(&rb).map_or(0, |nz| nz.get());
2507                            Some(shader_size)
2508                        }
2509                        _ => None,
2510                    })
2511                    .collect();
2512                pipeline::LateSizedBufferGroup { shader_sizes }
2513            })
2514            .collect()
2515    }
2516
2517    pub fn create_bind_group_layout(
2518        self: &Arc<Self>,
2519        desc: &binding_model::BindGroupLayoutDescriptor,
2520    ) -> Result<Arc<BindGroupLayout>, binding_model::CreateBindGroupLayoutError> {
2521        self.check_is_valid()?;
2522
2523        let entry_map = bgl::EntryMap::from_entries(&desc.entries)?;
2524
2525        let bgl_result = self.bgl_pool.get_or_init(entry_map, |entry_map| {
2526            let bgl =
2527                self.create_bind_group_layout_internal(&desc.label, entry_map, bgl::Origin::Pool)?;
2528            bgl.exclusive_pipeline
2529                .set(binding_model::ExclusivePipeline::None)
2530                .unwrap();
2531            Ok(bgl)
2532        });
2533
2534        match bgl_result {
2535            Ok(layout) => Ok(layout),
2536            Err(e) => Err(e),
2537        }
2538    }
2539
2540    fn create_bind_group_layout_internal(
2541        self: &Arc<Self>,
2542        label: &crate::Label,
2543        entry_map: bgl::EntryMap,
2544        origin: bgl::Origin,
2545    ) -> Result<Arc<BindGroupLayout>, binding_model::CreateBindGroupLayoutError> {
2546        #[derive(PartialEq)]
2547        enum WritableStorage {
2548            Yes,
2549            No,
2550        }
2551
2552        for entry in entry_map.values() {
2553            if entry.binding >= self.limits.max_bindings_per_bind_group {
2554                return Err(
2555                    binding_model::CreateBindGroupLayoutError::InvalidBindingIndex {
2556                        binding: entry.binding,
2557                        maximum: self.limits.max_bindings_per_bind_group,
2558                    },
2559                );
2560            }
2561
2562            use wgt::BindingType as Bt;
2563
2564            let mut required_features = wgt::Features::empty();
2565            let mut required_downlevel_flags = wgt::DownlevelFlags::empty();
2566            let (array_feature, writable_storage) = match entry.ty {
2567                Bt::Buffer {
2568                    ty: wgt::BufferBindingType::Uniform,
2569                    has_dynamic_offset: false,
2570                    min_binding_size: _,
2571                } => (
2572                    Some(wgt::Features::BUFFER_BINDING_ARRAY),
2573                    WritableStorage::No,
2574                ),
2575                Bt::Buffer {
2576                    ty: wgt::BufferBindingType::Uniform,
2577                    has_dynamic_offset: true,
2578                    min_binding_size: _,
2579                } => (
2580                    Some(wgt::Features::BUFFER_BINDING_ARRAY),
2581                    WritableStorage::No,
2582                ),
2583                Bt::Buffer {
2584                    ty: wgt::BufferBindingType::Storage { read_only },
2585                    ..
2586                } => (
2587                    Some(
2588                        wgt::Features::BUFFER_BINDING_ARRAY
2589                            | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
2590                    ),
2591                    match read_only {
2592                        true => WritableStorage::No,
2593                        false => WritableStorage::Yes,
2594                    },
2595                ),
2596                Bt::Sampler { .. } => (
2597                    Some(wgt::Features::TEXTURE_BINDING_ARRAY),
2598                    WritableStorage::No,
2599                ),
2600                Bt::Texture {
2601                    multisampled: true,
2602                    sample_type: TextureSampleType::Float { filterable: true },
2603                    ..
2604                } => {
2605                    return Err(binding_model::CreateBindGroupLayoutError::Entry {
2606                        binding: entry.binding,
2607                        error:
2608                            BindGroupLayoutEntryError::SampleTypeFloatFilterableBindingMultisampled,
2609                    });
2610                }
2611                Bt::Texture {
2612                    multisampled,
2613                    view_dimension,
2614                    ..
2615                } => {
2616                    if multisampled && view_dimension != TextureViewDimension::D2 {
2617                        return Err(binding_model::CreateBindGroupLayoutError::Entry {
2618                            binding: entry.binding,
2619                            error: BindGroupLayoutEntryError::Non2DMultisampled(view_dimension),
2620                        });
2621                    }
2622
2623                    (
2624                        Some(wgt::Features::TEXTURE_BINDING_ARRAY),
2625                        WritableStorage::No,
2626                    )
2627                }
2628                Bt::StorageTexture {
2629                    access,
2630                    view_dimension,
2631                    format,
2632                } => {
2633                    use wgt::{StorageTextureAccess as Access, TextureFormatFeatureFlags as Flags};
2634
2635                    match view_dimension {
2636                        TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
2637                            return Err(binding_model::CreateBindGroupLayoutError::Entry {
2638                                binding: entry.binding,
2639                                error: BindGroupLayoutEntryError::StorageTextureCube,
2640                            })
2641                        }
2642                        _ => (),
2643                    }
2644                    match access {
2645                        wgt::StorageTextureAccess::Atomic
2646                            if !self.features.contains(wgt::Features::TEXTURE_ATOMIC) =>
2647                        {
2648                            return Err(binding_model::CreateBindGroupLayoutError::Entry {
2649                                binding: entry.binding,
2650                                error: BindGroupLayoutEntryError::StorageTextureAtomic,
2651                            });
2652                        }
2653                        _ => (),
2654                    }
2655
2656                    let format_features =
2657                        self.describe_format_features(format).map_err(|error| {
2658                            binding_model::CreateBindGroupLayoutError::Entry {
2659                                binding: entry.binding,
2660                                error: BindGroupLayoutEntryError::MissingFeatures(error),
2661                            }
2662                        })?;
2663
2664                    let required_feature_flag = match access {
2665                        Access::WriteOnly => Flags::STORAGE_WRITE_ONLY,
2666                        Access::ReadOnly => Flags::STORAGE_READ_ONLY,
2667                        Access::ReadWrite => Flags::STORAGE_READ_WRITE,
2668                        Access::Atomic => Flags::STORAGE_ATOMIC,
2669                    };
2670
2671                    if !format_features.flags.contains(required_feature_flag) {
2672                        return Err(binding_model::CreateBindGroupLayoutError::UnsupportedStorageTextureAccess {
2673                            binding: entry.binding,
2674                            access,
2675                            format,
2676                        });
2677                    }
2678
2679                    (
2680                        Some(
2681                            wgt::Features::TEXTURE_BINDING_ARRAY
2682                                | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
2683                        ),
2684                        match access {
2685                            wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes,
2686                            wgt::StorageTextureAccess::ReadOnly => WritableStorage::No,
2687                            wgt::StorageTextureAccess::ReadWrite => WritableStorage::Yes,
2688                            wgt::StorageTextureAccess::Atomic => {
2689                                required_features |= wgt::Features::TEXTURE_ATOMIC;
2690                                WritableStorage::Yes
2691                            }
2692                        },
2693                    )
2694                }
2695                Bt::AccelerationStructure { vertex_return } => {
2696                    self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)
2697                        .map_err(|e| binding_model::CreateBindGroupLayoutError::Entry {
2698                            binding: entry.binding,
2699                            error: e.into(),
2700                        })?;
2701                    if vertex_return {
2702                        self.require_features(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN)
2703                            .map_err(|e| binding_model::CreateBindGroupLayoutError::Entry {
2704                                binding: entry.binding,
2705                                error: e.into(),
2706                            })?;
2707                    }
2708
2709                    (None, WritableStorage::No)
2710                }
2711                Bt::ExternalTexture => {
2712                    self.require_features(wgt::Features::EXTERNAL_TEXTURE)
2713                        .map_err(|e| binding_model::CreateBindGroupLayoutError::Entry {
2714                            binding: entry.binding,
2715                            error: e.into(),
2716                        })?;
2717                    (None, WritableStorage::No)
2718                }
2719            };
2720
2721            // Validate the count parameter
2722            if entry.count.is_some() {
2723                required_features |= array_feature
2724                    .ok_or(BindGroupLayoutEntryError::ArrayUnsupported)
2725                    .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
2726                        binding: entry.binding,
2727                        error,
2728                    })?;
2729            }
2730
2731            if entry.visibility.contains_unknown_bits() {
2732                return Err(
2733                    binding_model::CreateBindGroupLayoutError::InvalidVisibility(entry.visibility),
2734                );
2735            }
2736
2737            if entry.visibility.contains(wgt::ShaderStages::VERTEX) {
2738                if writable_storage == WritableStorage::Yes {
2739                    required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
2740                }
2741                if let Bt::Buffer {
2742                    ty: wgt::BufferBindingType::Storage { .. },
2743                    ..
2744                } = entry.ty
2745                {
2746                    required_downlevel_flags |= wgt::DownlevelFlags::VERTEX_STORAGE;
2747                }
2748            }
2749            if writable_storage == WritableStorage::Yes
2750                && entry.visibility.contains(wgt::ShaderStages::FRAGMENT)
2751            {
2752                required_downlevel_flags |= wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE;
2753            }
2754
2755            self.require_features(required_features)
2756                .map_err(BindGroupLayoutEntryError::MissingFeatures)
2757                .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
2758                    binding: entry.binding,
2759                    error,
2760                })?;
2761            self.require_downlevel_flags(required_downlevel_flags)
2762                .map_err(BindGroupLayoutEntryError::MissingDownlevelFlags)
2763                .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
2764                    binding: entry.binding,
2765                    error,
2766                })?;
2767        }
2768
2769        let bgl_flags = conv::bind_group_layout_flags(self.features);
2770
2771        let hal_bindings = entry_map.values().copied().collect::<Vec<_>>();
2772        let hal_desc = hal::BindGroupLayoutDescriptor {
2773            label: label.to_hal(self.instance_flags),
2774            flags: bgl_flags,
2775            entries: &hal_bindings,
2776        };
2777
2778        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
2779        for entry in entry_map.values() {
2780            count_validator.add_binding(entry);
2781        }
2782        // If a single bind group layout violates limits, the pipeline layout is
2783        // definitely going to violate limits too, lets catch it now.
2784        count_validator
2785            .validate(&self.limits)
2786            .map_err(binding_model::CreateBindGroupLayoutError::TooManyBindings)?;
2787
2788        // Validate that binding arrays don't conflict with dynamic offsets.
2789        count_validator.validate_binding_arrays()?;
2790
2791        let raw = unsafe { self.raw().create_bind_group_layout(&hal_desc) }
2792            .map_err(|e| self.handle_hal_error(e))?;
2793
2794        let bgl = BindGroupLayout {
2795            raw: binding_model::RawBindGroupLayout::Owning(ManuallyDrop::new(raw)),
2796            device: self.clone(),
2797            entries: entry_map,
2798            origin,
2799            exclusive_pipeline: OnceCellOrLock::new(),
2800            binding_count_validator: count_validator,
2801            label: label.to_string(),
2802        };
2803
2804        let bgl = Arc::new(bgl);
2805
2806        Ok(bgl)
2807    }
2808
2809    fn create_buffer_binding<'a>(
2810        &self,
2811        bb: &'a binding_model::ResolvedBufferBinding,
2812        binding: u32,
2813        decl: &wgt::BindGroupLayoutEntry,
2814        used_buffer_ranges: &mut Vec<BufferInitTrackerAction>,
2815        dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
2816        late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
2817        used: &mut BindGroupStates,
2818        snatch_guard: &'a SnatchGuard<'a>,
2819    ) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, binding_model::CreateBindGroupError>
2820    {
2821        use crate::binding_model::CreateBindGroupError as Error;
2822
2823        let (binding_ty, dynamic, min_size) = match decl.ty {
2824            wgt::BindingType::Buffer {
2825                ty,
2826                has_dynamic_offset,
2827                min_binding_size,
2828            } => (ty, has_dynamic_offset, min_binding_size),
2829            _ => {
2830                return Err(Error::WrongBindingType {
2831                    binding,
2832                    actual: decl.ty,
2833                    expected: "UniformBuffer, StorageBuffer or ReadonlyStorageBuffer",
2834                })
2835            }
2836        };
2837
2838        let (pub_usage, internal_use, range_limit) = match binding_ty {
2839            wgt::BufferBindingType::Uniform => (
2840                wgt::BufferUsages::UNIFORM,
2841                wgt::BufferUses::UNIFORM,
2842                self.limits.max_uniform_buffer_binding_size,
2843            ),
2844            wgt::BufferBindingType::Storage { read_only } => (
2845                wgt::BufferUsages::STORAGE,
2846                if read_only {
2847                    wgt::BufferUses::STORAGE_READ_ONLY
2848                } else {
2849                    wgt::BufferUses::STORAGE_READ_WRITE
2850                },
2851                self.limits.max_storage_buffer_binding_size,
2852            ),
2853        };
2854
2855        let (align, align_limit_name) =
2856            binding_model::buffer_binding_type_alignment(&self.limits, binding_ty);
2857        if !bb.offset.is_multiple_of(align as u64) {
2858            return Err(Error::UnalignedBufferOffset(
2859                bb.offset,
2860                align_limit_name,
2861                align,
2862            ));
2863        }
2864
2865        let buffer = &bb.buffer;
2866
2867        used.buffers.insert_single(buffer.clone(), internal_use);
2868
2869        buffer.same_device(self)?;
2870
2871        buffer.check_usage(pub_usage)?;
2872
2873        let req_size = match bb.size.map(wgt::BufferSize::new) {
2874            // Requested a non-zero size
2875            Some(non_zero @ Some(_)) => non_zero,
2876            // Requested size not specified
2877            None => None,
2878            // Requested zero size
2879            Some(None) => {
2880                return Err(binding_model::CreateBindGroupError::BindingZeroSize(
2881                    buffer.error_ident(),
2882                ))
2883            }
2884        };
2885        let (bb, bind_size) = buffer.binding(bb.offset, req_size, snatch_guard)?;
2886
2887        if matches!(binding_ty, wgt::BufferBindingType::Storage { .. })
2888            && bind_size % u64::from(wgt::STORAGE_BINDING_SIZE_ALIGNMENT) != 0
2889        {
2890            return Err(Error::UnalignedEffectiveBufferBindingSizeForStorage {
2891                alignment: wgt::STORAGE_BINDING_SIZE_ALIGNMENT,
2892                size: bind_size,
2893            });
2894        }
2895
2896        let bind_end = bb.offset + bind_size;
2897
2898        if bind_size > range_limit as u64 {
2899            return Err(Error::BufferRangeTooLarge {
2900                binding,
2901                given: bind_size as u32,
2902                limit: range_limit,
2903            });
2904        }
2905
2906        // Record binding info for validating dynamic offsets
2907        if dynamic {
2908            dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
2909                binding_idx: binding,
2910                buffer_size: buffer.size,
2911                binding_range: bb.offset..bind_end,
2912                maximum_dynamic_offset: buffer.size - bind_end,
2913                binding_type: binding_ty,
2914            });
2915        }
2916
2917        if let Some(non_zero) = min_size {
2918            let min_size = non_zero.get();
2919            if min_size > bind_size {
2920                return Err(Error::BindingSizeTooSmall {
2921                    buffer: buffer.error_ident(),
2922                    actual: bind_size,
2923                    min: min_size,
2924                });
2925            }
2926        } else {
2927            let late_size = wgt::BufferSize::new(bind_size)
2928                .ok_or_else(|| Error::BindingZeroSize(buffer.error_ident()))?;
2929            late_buffer_binding_sizes.insert(binding, late_size);
2930        }
2931
2932        // This was checked against the device's alignment requirements above,
2933        // which should always be a multiple of `COPY_BUFFER_ALIGNMENT`.
2934        assert_eq!(bb.offset % wgt::COPY_BUFFER_ALIGNMENT, 0);
2935
2936        // `wgpu_hal` only restricts shader access to bound buffer regions with
2937        // a certain resolution. For the sake of lazy initialization, round up
2938        // the size of the bound range to reflect how much of the buffer is
2939        // actually going to be visible to the shader.
2940        let bounds_check_alignment =
2941            binding_model::buffer_binding_type_bounds_check_alignment(&self.alignments, binding_ty);
2942        let visible_size = align_to(bind_size, bounds_check_alignment);
2943
2944        used_buffer_ranges.extend(buffer.initialization_status.read().create_action(
2945            buffer,
2946            bb.offset..bb.offset + visible_size,
2947            MemoryInitKind::NeedsInitializedMemory,
2948        ));
2949
2950        Ok(bb)
2951    }
2952
2953    fn create_sampler_binding<'a>(
2954        &self,
2955        used: &mut BindGroupStates,
2956        binding: u32,
2957        decl: &wgt::BindGroupLayoutEntry,
2958        sampler: &'a Arc<Sampler>,
2959    ) -> Result<&'a dyn hal::DynSampler, binding_model::CreateBindGroupError> {
2960        use crate::binding_model::CreateBindGroupError as Error;
2961
2962        used.samplers.insert_single(sampler.clone());
2963
2964        sampler.same_device(self)?;
2965
2966        match decl.ty {
2967            wgt::BindingType::Sampler(ty) => {
2968                let (allowed_filtering, allowed_comparison) = match ty {
2969                    wgt::SamplerBindingType::Filtering => (None, false),
2970                    wgt::SamplerBindingType::NonFiltering => (Some(false), false),
2971                    wgt::SamplerBindingType::Comparison => (None, true),
2972                };
2973                if let Some(allowed_filtering) = allowed_filtering {
2974                    if allowed_filtering != sampler.filtering {
2975                        return Err(Error::WrongSamplerFiltering {
2976                            binding,
2977                            layout_flt: allowed_filtering,
2978                            sampler_flt: sampler.filtering,
2979                        });
2980                    }
2981                }
2982                if allowed_comparison != sampler.comparison {
2983                    return Err(Error::WrongSamplerComparison {
2984                        binding,
2985                        layout_cmp: allowed_comparison,
2986                        sampler_cmp: sampler.comparison,
2987                    });
2988                }
2989            }
2990            _ => {
2991                return Err(Error::WrongBindingType {
2992                    binding,
2993                    actual: decl.ty,
2994                    expected: "Sampler",
2995                })
2996            }
2997        }
2998
2999        Ok(sampler.raw())
3000    }
3001
3002    fn create_texture_binding<'a>(
3003        &self,
3004        binding: u32,
3005        decl: &wgt::BindGroupLayoutEntry,
3006        view: &'a Arc<TextureView>,
3007        used: &mut BindGroupStates,
3008        used_texture_ranges: &mut Vec<TextureInitTrackerAction>,
3009        snatch_guard: &'a SnatchGuard<'a>,
3010    ) -> Result<hal::TextureBinding<'a, dyn hal::DynTextureView>, binding_model::CreateBindGroupError>
3011    {
3012        view.same_device(self)?;
3013
3014        let internal_use = self.texture_use_parameters(
3015            binding,
3016            decl,
3017            view,
3018            "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
3019        )?;
3020
3021        used.views.insert_single(view.clone(), internal_use);
3022
3023        let texture = &view.parent;
3024
3025        used_texture_ranges.push(TextureInitTrackerAction {
3026            texture: texture.clone(),
3027            range: TextureInitRange {
3028                mip_range: view.desc.range.mip_range(texture.desc.mip_level_count),
3029                layer_range: view
3030                    .desc
3031                    .range
3032                    .layer_range(texture.desc.array_layer_count()),
3033            },
3034            kind: MemoryInitKind::NeedsInitializedMemory,
3035        });
3036
3037        Ok(hal::TextureBinding {
3038            view: view.try_raw(snatch_guard)?,
3039            usage: internal_use,
3040        })
3041    }
3042
3043    fn create_tlas_binding<'a>(
3044        self: &Arc<Self>,
3045        used: &mut BindGroupStates,
3046        binding: u32,
3047        decl: &wgt::BindGroupLayoutEntry,
3048        tlas: &'a Arc<Tlas>,
3049        snatch_guard: &'a SnatchGuard<'a>,
3050    ) -> Result<&'a dyn hal::DynAccelerationStructure, binding_model::CreateBindGroupError> {
3051        use crate::binding_model::CreateBindGroupError as Error;
3052
3053        used.acceleration_structures.insert_single(tlas.clone());
3054
3055        tlas.same_device(self)?;
3056
3057        match decl.ty {
3058            wgt::BindingType::AccelerationStructure { vertex_return } => {
3059                if vertex_return
3060                    && !tlas.flags.contains(
3061                        wgpu_types::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,
3062                    )
3063                {
3064                    return Err(Error::MissingTLASVertexReturn { binding });
3065                }
3066            }
3067            _ => {
3068                return Err(Error::WrongBindingType {
3069                    binding,
3070                    actual: decl.ty,
3071                    expected: "Tlas",
3072                });
3073            }
3074        }
3075
3076        Ok(tlas.try_raw(snatch_guard)?)
3077    }
3078
3079    fn create_external_texture_binding<'a>(
3080        &'a self,
3081        binding: u32,
3082        decl: &wgt::BindGroupLayoutEntry,
3083        external_texture: &'a Arc<ExternalTexture>,
3084        used: &mut BindGroupStates,
3085        snatch_guard: &'a SnatchGuard,
3086    ) -> Result<
3087        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3088        binding_model::CreateBindGroupError,
3089    > {
3090        use crate::binding_model::CreateBindGroupError as Error;
3091
3092        external_texture.same_device(self)?;
3093
3094        used.external_textures
3095            .insert_single(external_texture.clone());
3096
3097        match decl.ty {
3098            wgt::BindingType::ExternalTexture => {}
3099            _ => {
3100                return Err(Error::WrongBindingType {
3101                    binding,
3102                    actual: decl.ty,
3103                    expected: "ExternalTexture",
3104                });
3105            }
3106        }
3107
3108        let planes = (0..3)
3109            .map(|i| {
3110                // We always need 3 bindings. If we have fewer than 3 planes
3111                // just bind plane 0 multiple times. The shader will only
3112                // sample from valid planes anyway.
3113                let plane = external_texture
3114                    .planes
3115                    .get(i)
3116                    .unwrap_or(&external_texture.planes[0]);
3117                let internal_use = wgt::TextureUses::RESOURCE;
3118                used.views.insert_single(plane.clone(), internal_use);
3119                let view = plane.try_raw(snatch_guard)?;
3120                Ok(hal::TextureBinding {
3121                    view,
3122                    usage: internal_use,
3123                })
3124            })
3125            // We can remove this intermediate Vec by using
3126            // array::try_from_fn() above, once it stabilizes.
3127            .collect::<Result<Vec<_>, Error>>()?;
3128        let planes = planes.try_into().unwrap();
3129
3130        used.buffers
3131            .insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);
3132        let params = external_texture.params.binding(0, None, snatch_guard)?.0;
3133
3134        Ok(hal::ExternalTextureBinding { planes, params })
3135    }
3136
3137    fn create_external_texture_binding_from_view<'a>(
3138        &'a self,
3139        binding: u32,
3140        decl: &wgt::BindGroupLayoutEntry,
3141        view: &'a Arc<TextureView>,
3142        used: &mut BindGroupStates,
3143        snatch_guard: &'a SnatchGuard,
3144    ) -> Result<
3145        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3146        binding_model::CreateBindGroupError,
3147    > {
3148        use crate::binding_model::CreateBindGroupError as Error;
3149
3150        view.same_device(self)?;
3151
3152        let internal_use = self.texture_use_parameters(binding, decl, view, "SampledTexture")?;
3153        used.views.insert_single(view.clone(), internal_use);
3154
3155        match decl.ty {
3156            wgt::BindingType::ExternalTexture => {}
3157            _ => {
3158                return Err(Error::WrongBindingType {
3159                    binding,
3160                    actual: decl.ty,
3161                    expected: "ExternalTexture",
3162                });
3163            }
3164        }
3165
3166        // We need 3 bindings, so just repeat the same texture view 3 times.
3167        let planes = [
3168            hal::TextureBinding {
3169                view: view.try_raw(snatch_guard)?,
3170                usage: internal_use,
3171            },
3172            hal::TextureBinding {
3173                view: view.try_raw(snatch_guard)?,
3174                usage: internal_use,
3175            },
3176            hal::TextureBinding {
3177                view: view.try_raw(snatch_guard)?,
3178                usage: internal_use,
3179            },
3180        ];
3181        let params = hal::BufferBinding::new_unchecked(
3182            self.default_external_texture_params_buffer.as_ref(),
3183            0,
3184            None,
3185        );
3186
3187        Ok(hal::ExternalTextureBinding { planes, params })
3188    }
3189
3190    // This function expects the provided bind group layout to be resolved
3191    // (not passing a duplicate) beforehand.
3192    pub fn create_bind_group(
3193        self: &Arc<Self>,
3194        desc: binding_model::ResolvedBindGroupDescriptor,
3195    ) -> Result<Arc<BindGroup>, binding_model::CreateBindGroupError> {
3196        use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};
3197
3198        let layout = desc.layout;
3199
3200        self.check_is_valid()?;
3201        layout.same_device(self)?;
3202
3203        {
3204            // Check that the number of entries in the descriptor matches
3205            // the number of entries in the layout.
3206            let actual = desc.entries.len();
3207            let expected = layout.entries.len();
3208            if actual != expected {
3209                return Err(Error::BindingsNumMismatch { expected, actual });
3210            }
3211        }
3212
3213        // TODO: arrayvec/smallvec, or re-use allocations
3214        // Record binding info for dynamic offset validation
3215        let mut dynamic_binding_info = Vec::new();
3216        // Map of binding -> shader reflected size
3217        //Note: we can't collect into a vector right away because
3218        // it needs to be in BGL iteration order, not BG entry order.
3219        let mut late_buffer_binding_sizes = FastHashMap::default();
3220        // fill out the descriptors
3221        let mut used = BindGroupStates::new();
3222
3223        let mut used_buffer_ranges = Vec::new();
3224        let mut used_texture_ranges = Vec::new();
3225        let mut hal_entries = Vec::with_capacity(desc.entries.len());
3226        let mut hal_buffers = Vec::new();
3227        let mut hal_samplers = Vec::new();
3228        let mut hal_textures = Vec::new();
3229        let mut hal_tlas_s = Vec::new();
3230        let mut hal_external_textures = Vec::new();
3231        let snatch_guard = self.snatchable_lock.read();
3232        for entry in desc.entries.iter() {
3233            let binding = entry.binding;
3234            // Find the corresponding declaration in the layout
3235            let decl = layout
3236                .entries
3237                .get(binding)
3238                .ok_or(Error::MissingBindingDeclaration(binding))?;
3239            let (res_index, count) = match entry.resource {
3240                Br::Buffer(ref bb) => {
3241                    let bb = self.create_buffer_binding(
3242                        bb,
3243                        binding,
3244                        decl,
3245                        &mut used_buffer_ranges,
3246                        &mut dynamic_binding_info,
3247                        &mut late_buffer_binding_sizes,
3248                        &mut used,
3249                        &snatch_guard,
3250                    )?;
3251
3252                    let res_index = hal_buffers.len();
3253                    hal_buffers.push(bb);
3254                    (res_index, 1)
3255                }
3256                Br::BufferArray(ref bindings_array) => {
3257                    let num_bindings = bindings_array.len();
3258                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3259
3260                    let res_index = hal_buffers.len();
3261                    for bb in bindings_array.iter() {
3262                        let bb = self.create_buffer_binding(
3263                            bb,
3264                            binding,
3265                            decl,
3266                            &mut used_buffer_ranges,
3267                            &mut dynamic_binding_info,
3268                            &mut late_buffer_binding_sizes,
3269                            &mut used,
3270                            &snatch_guard,
3271                        )?;
3272                        hal_buffers.push(bb);
3273                    }
3274                    (res_index, num_bindings)
3275                }
3276                Br::Sampler(ref sampler) => {
3277                    let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3278
3279                    let res_index = hal_samplers.len();
3280                    hal_samplers.push(sampler);
3281                    (res_index, 1)
3282                }
3283                Br::SamplerArray(ref samplers) => {
3284                    let num_bindings = samplers.len();
3285                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3286
3287                    let res_index = hal_samplers.len();
3288                    for sampler in samplers.iter() {
3289                        let sampler =
3290                            self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3291
3292                        hal_samplers.push(sampler);
3293                    }
3294
3295                    (res_index, num_bindings)
3296                }
3297                Br::TextureView(ref view) => match decl.ty {
3298                    wgt::BindingType::ExternalTexture => {
3299                        let et = self.create_external_texture_binding_from_view(
3300                            binding,
3301                            decl,
3302                            view,
3303                            &mut used,
3304                            &snatch_guard,
3305                        )?;
3306                        let res_index = hal_external_textures.len();
3307                        hal_external_textures.push(et);
3308                        (res_index, 1)
3309                    }
3310                    _ => {
3311                        let tb = self.create_texture_binding(
3312                            binding,
3313                            decl,
3314                            view,
3315                            &mut used,
3316                            &mut used_texture_ranges,
3317                            &snatch_guard,
3318                        )?;
3319                        let res_index = hal_textures.len();
3320                        hal_textures.push(tb);
3321                        (res_index, 1)
3322                    }
3323                },
3324                Br::TextureViewArray(ref views) => {
3325                    let num_bindings = views.len();
3326                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3327
3328                    let res_index = hal_textures.len();
3329                    for view in views.iter() {
3330                        let tb = self.create_texture_binding(
3331                            binding,
3332                            decl,
3333                            view,
3334                            &mut used,
3335                            &mut used_texture_ranges,
3336                            &snatch_guard,
3337                        )?;
3338
3339                        hal_textures.push(tb);
3340                    }
3341
3342                    (res_index, num_bindings)
3343                }
3344                Br::AccelerationStructure(ref tlas) => {
3345                    let tlas =
3346                        self.create_tlas_binding(&mut used, binding, decl, tlas, &snatch_guard)?;
3347                    let res_index = hal_tlas_s.len();
3348                    hal_tlas_s.push(tlas);
3349                    (res_index, 1)
3350                }
3351                Br::ExternalTexture(ref et) => {
3352                    let et = self.create_external_texture_binding(
3353                        binding,
3354                        decl,
3355                        et,
3356                        &mut used,
3357                        &snatch_guard,
3358                    )?;
3359                    let res_index = hal_external_textures.len();
3360                    hal_external_textures.push(et);
3361                    (res_index, 1)
3362                }
3363            };
3364
3365            hal_entries.push(hal::BindGroupEntry {
3366                binding,
3367                resource_index: res_index as u32,
3368                count: count as u32,
3369            });
3370        }
3371
3372        used.optimize();
3373
3374        hal_entries.sort_by_key(|entry| entry.binding);
3375        for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
3376            if a.binding == b.binding {
3377                return Err(Error::DuplicateBinding(a.binding));
3378            }
3379        }
3380        let hal_desc = hal::BindGroupDescriptor {
3381            label: desc.label.to_hal(self.instance_flags),
3382            layout: layout.raw(),
3383            entries: &hal_entries,
3384            buffers: &hal_buffers,
3385            samplers: &hal_samplers,
3386            textures: &hal_textures,
3387            acceleration_structures: &hal_tlas_s,
3388            external_textures: &hal_external_textures,
3389        };
3390        let raw = unsafe { self.raw().create_bind_group(&hal_desc) }
3391            .map_err(|e| self.handle_hal_error(e))?;
3392
3393        // collect in the order of BGL iteration
3394        let late_buffer_binding_infos = layout
3395            .entries
3396            .indices()
3397            .flat_map(|binding| {
3398                let size = late_buffer_binding_sizes.get(&binding).cloned()?;
3399                Some(BindGroupLateBufferBindingInfo {
3400                    binding_index: binding,
3401                    size,
3402                })
3403            })
3404            .collect();
3405
3406        let bind_group = BindGroup {
3407            raw: Snatchable::new(raw),
3408            device: self.clone(),
3409            layout,
3410            label: desc.label.to_string(),
3411            tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),
3412            used,
3413            used_buffer_ranges,
3414            used_texture_ranges,
3415            dynamic_binding_info,
3416            late_buffer_binding_infos,
3417        };
3418
3419        let bind_group = Arc::new(bind_group);
3420
3421        let weak_ref = Arc::downgrade(&bind_group);
3422        for range in &bind_group.used_texture_ranges {
3423            let mut bind_groups = range.texture.bind_groups.lock();
3424            bind_groups.push(weak_ref.clone());
3425        }
3426        for range in &bind_group.used_buffer_ranges {
3427            let mut bind_groups = range.buffer.bind_groups.lock();
3428            bind_groups.push(weak_ref.clone());
3429        }
3430
3431        Ok(bind_group)
3432    }
3433
3434    fn check_array_binding(
3435        features: wgt::Features,
3436        count: Option<NonZeroU32>,
3437        num_bindings: usize,
3438    ) -> Result<(), binding_model::CreateBindGroupError> {
3439        use super::binding_model::CreateBindGroupError as Error;
3440
3441        if let Some(count) = count {
3442            let count = count.get() as usize;
3443            if count < num_bindings {
3444                return Err(Error::BindingArrayPartialLengthMismatch {
3445                    actual: num_bindings,
3446                    expected: count,
3447                });
3448            }
3449            if count != num_bindings
3450                && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
3451            {
3452                return Err(Error::BindingArrayLengthMismatch {
3453                    actual: num_bindings,
3454                    expected: count,
3455                });
3456            }
3457            if num_bindings == 0 {
3458                return Err(Error::BindingArrayZeroLength);
3459            }
3460        } else {
3461            return Err(Error::SingleBindingExpected);
3462        };
3463
3464        Ok(())
3465    }
3466
3467    fn texture_use_parameters(
3468        &self,
3469        binding: u32,
3470        decl: &wgt::BindGroupLayoutEntry,
3471        view: &TextureView,
3472        expected: &'static str,
3473    ) -> Result<wgt::TextureUses, binding_model::CreateBindGroupError> {
3474        use crate::binding_model::CreateBindGroupError as Error;
3475        if view
3476            .desc
3477            .aspects()
3478            .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
3479        {
3480            return Err(Error::DepthStencilAspect);
3481        }
3482        match decl.ty {
3483            wgt::BindingType::Texture {
3484                sample_type,
3485                view_dimension,
3486                multisampled,
3487            } => {
3488                use wgt::TextureSampleType as Tst;
3489                if multisampled != (view.samples != 1) {
3490                    return Err(Error::InvalidTextureMultisample {
3491                        binding,
3492                        layout_multisampled: multisampled,
3493                        view_samples: view.samples,
3494                    });
3495                }
3496                let compat_sample_type = view
3497                    .desc
3498                    .format
3499                    .sample_type(Some(view.desc.range.aspect), Some(self.features))
3500                    .unwrap();
3501                match (sample_type, compat_sample_type) {
3502                    (Tst::Uint, Tst::Uint) |
3503                        (Tst::Sint, Tst::Sint) |
3504                        (Tst::Depth, Tst::Depth) |
3505                        // if we expect non-filterable, accept anything float
3506                        (Tst::Float { filterable: false }, Tst::Float { .. }) |
3507                        // if we expect filterable, require it
3508                        (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
3509                        // if we expect non-filterable, also accept depth
3510                        (Tst::Float { filterable: false }, Tst::Depth) => {}
3511                    // if we expect filterable, also accept Float that is defined as
3512                    // unfilterable if filterable feature is explicitly enabled (only hit
3513                    // if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is
3514                    // enabled)
3515                    (Tst::Float { filterable: true }, Tst::Float { .. }) if view.format_features.flags.contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
3516                    _ => {
3517                        return Err(Error::InvalidTextureSampleType {
3518                            binding,
3519                            layout_sample_type: sample_type,
3520                            view_format: view.desc.format,
3521                            view_sample_type: compat_sample_type,
3522                        })
3523                    }
3524                }
3525                if view_dimension != view.desc.dimension {
3526                    return Err(Error::InvalidTextureDimension {
3527                        binding,
3528                        layout_dimension: view_dimension,
3529                        view_dimension: view.desc.dimension,
3530                    });
3531                }
3532                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3533                Ok(wgt::TextureUses::RESOURCE)
3534            }
3535            wgt::BindingType::StorageTexture {
3536                access,
3537                format,
3538                view_dimension,
3539            } => {
3540                if format != view.desc.format {
3541                    return Err(Error::InvalidStorageTextureFormat {
3542                        binding,
3543                        layout_format: format,
3544                        view_format: view.desc.format,
3545                    });
3546                }
3547                if view_dimension != view.desc.dimension {
3548                    return Err(Error::InvalidTextureDimension {
3549                        binding,
3550                        layout_dimension: view_dimension,
3551                        view_dimension: view.desc.dimension,
3552                    });
3553                }
3554
3555                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3556                if mip_level_count != 1 {
3557                    return Err(Error::InvalidStorageTextureMipLevelCount {
3558                        binding,
3559                        mip_level_count,
3560                    });
3561                }
3562
3563                view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;
3564
3565                Ok(match access {
3566                    wgt::StorageTextureAccess::ReadOnly => wgt::TextureUses::STORAGE_READ_ONLY,
3567                    wgt::StorageTextureAccess::WriteOnly => wgt::TextureUses::STORAGE_WRITE_ONLY,
3568                    wgt::StorageTextureAccess::ReadWrite => wgt::TextureUses::STORAGE_READ_WRITE,
3569                    wgt::StorageTextureAccess::Atomic => wgt::TextureUses::STORAGE_ATOMIC,
3570                })
3571            }
3572            wgt::BindingType::ExternalTexture => {
3573                if view.desc.dimension != TextureViewDimension::D2 {
3574                    return Err(Error::InvalidTextureDimension {
3575                        binding,
3576                        layout_dimension: TextureViewDimension::D2,
3577                        view_dimension: view.desc.dimension,
3578                    });
3579                }
3580                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3581                if mip_level_count != 1 {
3582                    return Err(Error::InvalidExternalTextureMipLevelCount {
3583                        binding,
3584                        mip_level_count,
3585                    });
3586                }
3587                if view.desc.format != TextureFormat::Rgba8Unorm
3588                    && view.desc.format != TextureFormat::Bgra8Unorm
3589                    && view.desc.format != TextureFormat::Rgba16Float
3590                {
3591                    return Err(Error::InvalidExternalTextureFormat {
3592                        binding,
3593                        format: view.desc.format,
3594                    });
3595                }
3596                if view.samples != 1 {
3597                    return Err(Error::InvalidTextureMultisample {
3598                        binding,
3599                        layout_multisampled: false,
3600                        view_samples: view.samples,
3601                    });
3602                }
3603
3604                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3605                Ok(wgt::TextureUses::RESOURCE)
3606            }
3607            _ => Err(Error::WrongBindingType {
3608                binding,
3609                actual: decl.ty,
3610                expected,
3611            }),
3612        }
3613    }
3614
3615    pub fn create_pipeline_layout(
3616        self: &Arc<Self>,
3617        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3618    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
3619        self.create_pipeline_layout_impl(desc, false)
3620    }
3621
3622    fn create_pipeline_layout_impl(
3623        self: &Arc<Self>,
3624        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3625        ignore_exclusive_pipeline_check: bool,
3626    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
3627        use crate::binding_model::CreatePipelineLayoutError as Error;
3628
3629        self.check_is_valid()?;
3630
3631        let bind_group_layouts_count = desc.bind_group_layouts.len();
3632        let device_max_bind_groups = self.limits.max_bind_groups as usize;
3633        if bind_group_layouts_count > device_max_bind_groups {
3634            return Err(Error::TooManyGroups {
3635                actual: bind_group_layouts_count,
3636                max: device_max_bind_groups,
3637            });
3638        }
3639
3640        if desc.immediate_size != 0 {
3641            self.require_features(wgt::Features::IMMEDIATES)?;
3642        }
3643        if self.limits.max_immediate_size < desc.immediate_size {
3644            return Err(Error::ImmediateRangeTooLarge {
3645                size: desc.immediate_size,
3646                max: self.limits.max_immediate_size,
3647            });
3648        }
3649        if !desc
3650            .immediate_size
3651            .is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT)
3652        {
3653            return Err(Error::MisalignedImmediateSize {
3654                size: desc.immediate_size,
3655            });
3656        }
3657
3658        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
3659
3660        for (index, bgl) in desc.bind_group_layouts.iter().enumerate() {
3661            let Some(bgl) = bgl else {
3662                continue;
3663            };
3664
3665            bgl.same_device(self)?;
3666
3667            if !ignore_exclusive_pipeline_check {
3668                let exclusive_pipeline = bgl.exclusive_pipeline.get().unwrap();
3669                if !matches!(exclusive_pipeline, binding_model::ExclusivePipeline::None) {
3670                    return Err(Error::BglHasExclusivePipeline {
3671                        index,
3672                        pipeline: alloc::format!("{exclusive_pipeline}"),
3673                    });
3674                }
3675            }
3676
3677            count_validator.merge(&bgl.binding_count_validator);
3678        }
3679
3680        count_validator
3681            .validate(&self.limits)
3682            .map_err(Error::TooManyBindings)?;
3683
3684        let get_bgl_iter = || {
3685            desc.bind_group_layouts
3686                .iter()
3687                .map(|bgl| bgl.as_ref().filter(|bgl| !bgl.entries.is_empty()))
3688        };
3689
3690        let bind_group_layouts = get_bgl_iter()
3691            .map(|bgl| bgl.cloned())
3692            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3693
3694        let raw_bind_group_layouts = get_bgl_iter()
3695            .map(|bgl| bgl.map(|bgl| bgl.raw()))
3696            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3697
3698        let additional_flags = if self.indirect_validation.is_some() {
3699            hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE
3700        } else {
3701            hal::PipelineLayoutFlags::empty()
3702        };
3703
3704        let hal_desc = hal::PipelineLayoutDescriptor {
3705            label: desc.label.to_hal(self.instance_flags),
3706            flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
3707                | hal::PipelineLayoutFlags::NUM_WORK_GROUPS
3708                | additional_flags,
3709            bind_group_layouts: &raw_bind_group_layouts,
3710            immediate_size: desc.immediate_size,
3711        };
3712
3713        let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }
3714            .map_err(|e| self.handle_hal_error(e))?;
3715
3716        drop(raw_bind_group_layouts);
3717
3718        let layout = binding_model::PipelineLayout {
3719            raw: ManuallyDrop::new(raw),
3720            device: self.clone(),
3721            label: desc.label.to_string(),
3722            bind_group_layouts,
3723            immediate_size: desc.immediate_size,
3724        };
3725
3726        let layout = Arc::new(layout);
3727
3728        Ok(layout)
3729    }
3730
3731    fn create_derived_pipeline_layout(
3732        self: &Arc<Self>,
3733        mut derived_group_layouts: Box<ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>>,
3734    ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {
3735        while derived_group_layouts
3736            .last()
3737            .is_some_and(|map| map.is_empty())
3738        {
3739            derived_group_layouts.pop();
3740        }
3741
3742        let mut unique_bind_group_layouts = FastHashMap::default();
3743
3744        let bind_group_layouts = derived_group_layouts
3745            .into_iter()
3746            .map(|mut bgl_entry_map| {
3747                if bgl_entry_map.is_empty() {
3748                    return Ok(None);
3749                }
3750
3751                bgl_entry_map.sort();
3752                match unique_bind_group_layouts.entry(bgl_entry_map) {
3753                    hashbrown::hash_map::Entry::Occupied(v) => Ok(Some(Arc::clone(v.get()))),
3754                    hashbrown::hash_map::Entry::Vacant(e) => {
3755                        match self.create_bind_group_layout_internal(
3756                            &None,
3757                            e.key().clone(),
3758                            bgl::Origin::Derived,
3759                        ) {
3760                            Ok(bgl) => {
3761                                e.insert(bgl.clone());
3762                                Ok(Some(bgl))
3763                            }
3764                            Err(e) => Err(e),
3765                        }
3766                    }
3767                }
3768            })
3769            .collect::<Result<Vec<_>, _>>()?;
3770
3771        let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {
3772            label: None,
3773            bind_group_layouts: Cow::Owned(bind_group_layouts),
3774            immediate_size: 0, //TODO?
3775        };
3776
3777        let layout = self.create_pipeline_layout_impl(&layout_desc, true)?;
3778        Ok(layout)
3779    }
3780
3781    pub fn create_compute_pipeline(
3782        self: &Arc<Self>,
3783        desc: pipeline::ResolvedComputePipelineDescriptor,
3784    ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {
3785        self.check_is_valid()?;
3786
3787        self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
3788
3789        let shader_module = desc.stage.module;
3790
3791        shader_module.same_device(self)?;
3792
3793        let is_auto_layout = desc.layout.is_none();
3794
3795        // Get the pipeline layout from the desc if it is provided.
3796        let pipeline_layout = match desc.layout {
3797            Some(pipeline_layout) => {
3798                pipeline_layout.same_device(self)?;
3799                Some(pipeline_layout)
3800            }
3801            None => None,
3802        };
3803
3804        let mut binding_layout_source = match pipeline_layout {
3805            Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
3806            None => validation::BindingLayoutSource::new_derived(&self.limits),
3807        };
3808        let mut shader_binding_sizes = FastHashMap::default();
3809        let io = validation::StageIo::default();
3810
3811        let final_entry_point_name;
3812
3813        {
3814            let stage = validation::ShaderStageForValidation::Compute;
3815
3816            final_entry_point_name = shader_module.finalize_entry_point_name(
3817                stage.to_naga(),
3818                desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
3819            )?;
3820
3821            if let Some(ref interface) = shader_module.interface {
3822                let _ = interface.check_stage(
3823                    &mut binding_layout_source,
3824                    &mut shader_binding_sizes,
3825                    &final_entry_point_name,
3826                    stage,
3827                    io,
3828                )?;
3829            }
3830        }
3831
3832        let pipeline_layout = match binding_layout_source {
3833            validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
3834            validation::BindingLayoutSource::Derived(entries) => {
3835                self.create_derived_pipeline_layout(entries)?
3836            }
3837        };
3838
3839        let late_sized_buffer_groups =
3840            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
3841
3842        let cache = match desc.cache {
3843            Some(cache) => {
3844                cache.same_device(self)?;
3845                Some(cache)
3846            }
3847            None => None,
3848        };
3849
3850        let pipeline_desc = hal::ComputePipelineDescriptor {
3851            label: desc.label.to_hal(self.instance_flags),
3852            layout: pipeline_layout.raw(),
3853            stage: hal::ProgrammableStage {
3854                module: shader_module.raw(),
3855                entry_point: final_entry_point_name.as_ref(),
3856                constants: &desc.stage.constants,
3857                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
3858            },
3859            cache: cache.as_ref().map(|it| it.raw()),
3860        };
3861
3862        let raw =
3863            unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(
3864                |err| match err {
3865                    hal::PipelineError::Device(error) => {
3866                        pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))
3867                    }
3868                    hal::PipelineError::Linkage(_stages, msg) => {
3869                        pipeline::CreateComputePipelineError::Internal(msg)
3870                    }
3871                    hal::PipelineError::EntryPoint(_stage) => {
3872                        pipeline::CreateComputePipelineError::Internal(
3873                            ENTRYPOINT_FAILURE_ERROR.to_string(),
3874                        )
3875                    }
3876                    hal::PipelineError::PipelineConstants(_stages, msg) => {
3877                        pipeline::CreateComputePipelineError::PipelineConstants(msg)
3878                    }
3879                },
3880            )?;
3881
3882        let pipeline = pipeline::ComputePipeline {
3883            raw: ManuallyDrop::new(raw),
3884            layout: pipeline_layout,
3885            device: self.clone(),
3886            _shader_module: shader_module,
3887            late_sized_buffer_groups,
3888            label: desc.label.to_string(),
3889            tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),
3890        };
3891
3892        let pipeline = Arc::new(pipeline);
3893
3894        if is_auto_layout {
3895            for bgl in pipeline.layout.bind_group_layouts.iter() {
3896                let Some(bgl) = bgl else {
3897                    continue;
3898                };
3899
3900                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
3901                let _ = bgl
3902                    .exclusive_pipeline
3903                    .set(binding_model::ExclusivePipeline::Compute(Arc::downgrade(
3904                        &pipeline,
3905                    )));
3906            }
3907        }
3908
3909        Ok(pipeline)
3910    }
3911
3912    pub fn create_render_pipeline(
3913        self: &Arc<Self>,
3914        desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
3915    ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {
3916        use wgt::TextureFormatFeatureFlags as Tfff;
3917
3918        self.check_is_valid()?;
3919
3920        let mut shader_binding_sizes = FastHashMap::default();
3921
3922        let num_attachments = desc.fragment.as_ref().map(|f| f.targets.len()).unwrap_or(0);
3923        let max_attachments = self.limits.max_color_attachments as usize;
3924        if num_attachments > max_attachments {
3925            return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
3926                command::ColorAttachmentError::TooMany {
3927                    given: num_attachments,
3928                    limit: max_attachments,
3929                },
3930            ));
3931        }
3932
3933        let color_targets = desc
3934            .fragment
3935            .as_ref()
3936            .map_or(&[][..], |fragment| &fragment.targets);
3937        let depth_stencil_state = desc.depth_stencil.as_ref();
3938
3939        {
3940            let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
3941                color_targets.iter().filter_map(|x| x.as_ref()).collect();
3942            if !cts.is_empty() && {
3943                let first = &cts[0];
3944                cts[1..]
3945                    .iter()
3946                    .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
3947            } {
3948                self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
3949            }
3950        }
3951
3952        let mut io = validation::StageIo::default();
3953        let mut validated_stages = wgt::ShaderStages::empty();
3954
3955        let mut vertex_steps;
3956        let mut vertex_buffers;
3957        let mut total_attributes;
3958        let mut dual_source_blending = false;
3959        let mut has_depth_attachment = false;
3960        if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
3961            vertex_steps = Vec::with_capacity(vertex.buffers.len());
3962            vertex_buffers = Vec::with_capacity(vertex.buffers.len());
3963            total_attributes = 0;
3964            for (i, vb_state) in vertex.buffers.iter().enumerate() {
3965                // https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpuvertexbufferlayout
3966
3967                if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
3968                    return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
3969                        index: i as u32,
3970                        given: vb_state.array_stride as u32,
3971                        limit: self.limits.max_vertex_buffer_array_stride,
3972                    });
3973                }
3974                if vb_state.array_stride % wgt::VERTEX_ALIGNMENT != 0 {
3975                    return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
3976                        index: i as u32,
3977                        stride: vb_state.array_stride,
3978                    });
3979                }
3980
3981                let max_stride = if vb_state.array_stride == 0 {
3982                    self.limits.max_vertex_buffer_array_stride as u64
3983                } else {
3984                    vb_state.array_stride
3985                };
3986                let mut last_stride = 0;
3987                for attribute in vb_state.attributes.iter() {
3988                    let attribute_stride = attribute.offset + attribute.format.size();
3989                    if attribute_stride > max_stride {
3990                        return Err(
3991                            pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge {
3992                                location: attribute.shader_location,
3993                                given: attribute_stride as u32,
3994                                limit: max_stride as u32,
3995                            },
3996                        );
3997                    }
3998
3999                    let required_offset_alignment = attribute.format.size().min(4);
4000                    if attribute.offset % required_offset_alignment != 0 {
4001                        return Err(
4002                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4003                                location: attribute.shader_location,
4004                                offset: attribute.offset,
4005                            },
4006                        );
4007                    }
4008
4009                    if attribute.shader_location >= self.limits.max_vertex_attributes {
4010                        return Err(
4011                            pipeline::CreateRenderPipelineError::VertexAttributeLocationTooLarge {
4012                                given: attribute.shader_location,
4013                                limit: self.limits.max_vertex_attributes,
4014                            },
4015                        );
4016                    }
4017
4018                    last_stride = last_stride.max(attribute_stride);
4019                }
4020                vertex_steps.push(pipeline::VertexStep {
4021                    stride: vb_state.array_stride,
4022                    last_stride,
4023                    mode: vb_state.step_mode,
4024                });
4025                if vb_state.attributes.is_empty() {
4026                    continue;
4027                }
4028                vertex_buffers.push(hal::VertexBufferLayout {
4029                    array_stride: vb_state.array_stride,
4030                    step_mode: vb_state.step_mode,
4031                    attributes: vb_state.attributes.as_ref(),
4032                });
4033
4034                for attribute in vb_state.attributes.iter() {
4035                    if attribute.offset >= 0x10000000 {
4036                        return Err(
4037                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4038                                location: attribute.shader_location,
4039                                offset: attribute.offset,
4040                            },
4041                        );
4042                    }
4043
4044                    if let wgt::VertexFormat::Float64
4045                    | wgt::VertexFormat::Float64x2
4046                    | wgt::VertexFormat::Float64x3
4047                    | wgt::VertexFormat::Float64x4 = attribute.format
4048                    {
4049                        self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
4050                    }
4051
4052                    let previous = io.varyings.insert(
4053                        attribute.shader_location,
4054                        validation::InterfaceVar::vertex_attribute(attribute.format),
4055                    );
4056
4057                    if previous.is_some() {
4058                        return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
4059                            attribute.shader_location,
4060                        ));
4061                    }
4062                }
4063                total_attributes += vb_state.attributes.len();
4064            }
4065
4066            if vertex_buffers.len() > self.limits.max_vertex_buffers as usize {
4067                return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
4068                    given: vertex_buffers.len() as u32,
4069                    limit: self.limits.max_vertex_buffers,
4070                });
4071            }
4072            if total_attributes > self.limits.max_vertex_attributes as usize {
4073                return Err(
4074                    pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
4075                        given: total_attributes as u32,
4076                        limit: self.limits.max_vertex_attributes,
4077                    },
4078                );
4079            }
4080        } else {
4081            vertex_steps = Vec::new();
4082            vertex_buffers = Vec::new();
4083        };
4084
4085        if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
4086            return Err(
4087                pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
4088                    strip_index_format: desc.primitive.strip_index_format,
4089                    topology: desc.primitive.topology,
4090                },
4091            );
4092        }
4093
4094        if desc.primitive.unclipped_depth {
4095            self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
4096        }
4097
4098        if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
4099            self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
4100        }
4101        if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
4102            self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
4103        }
4104
4105        if desc.primitive.conservative {
4106            self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
4107        }
4108
4109        if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
4110            return Err(
4111                pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
4112            );
4113        }
4114
4115        let mut target_specified = false;
4116
4117        for (i, cs) in color_targets.iter().enumerate() {
4118            if let Some(cs) = cs.as_ref() {
4119                target_specified = true;
4120                let error = 'error: {
4121                    if cs.write_mask.contains_unknown_bits() {
4122                        break 'error Some(pipeline::ColorStateError::InvalidWriteMask(
4123                            cs.write_mask,
4124                        ));
4125                    }
4126
4127                    let format_features = self.describe_format_features(cs.format)?;
4128                    if !format_features
4129                        .allowed_usages
4130                        .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4131                    {
4132                        break 'error Some(pipeline::ColorStateError::FormatNotRenderable(
4133                            cs.format,
4134                        ));
4135                    }
4136                    if cs.blend.is_some() && !format_features.flags.contains(Tfff::BLENDABLE) {
4137                        break 'error Some(pipeline::ColorStateError::FormatNotBlendable(
4138                            cs.format,
4139                        ));
4140                    }
4141                    if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
4142                        break 'error Some(pipeline::ColorStateError::FormatNotColor(cs.format));
4143                    }
4144
4145                    if desc.multisample.count > 1
4146                        && !format_features
4147                            .flags
4148                            .sample_count_supported(desc.multisample.count)
4149                    {
4150                        break 'error Some(pipeline::ColorStateError::InvalidSampleCount(
4151                            desc.multisample.count,
4152                            cs.format,
4153                            cs.format
4154                                .guaranteed_format_features(self.features)
4155                                .flags
4156                                .supported_sample_counts(),
4157                            self.adapter
4158                                .get_texture_format_features(cs.format)
4159                                .flags
4160                                .supported_sample_counts(),
4161                        ));
4162                    }
4163
4164                    if let Some(blend_mode) = cs.blend {
4165                        for component in [&blend_mode.color, &blend_mode.alpha] {
4166                            for factor in [component.src_factor, component.dst_factor] {
4167                                if factor.ref_second_blend_source() {
4168                                    self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
4169                                    if i == 0 {
4170                                        dual_source_blending = true;
4171                                    } else {
4172                                        break 'error Some(
4173                                            pipeline::ColorStateError::BlendFactorOnUnsupportedTarget {
4174                                                factor,
4175                                                target: i as u32,
4176                                            }
4177                                        );
4178                                    }
4179                                }
4180
4181                                if [wgt::BlendOperation::Min, wgt::BlendOperation::Max]
4182                                    .contains(&component.operation)
4183                                    && factor != wgt::BlendFactor::One
4184                                {
4185                                    break 'error Some(
4186                                        pipeline::ColorStateError::InvalidMinMaxBlendFactor {
4187                                            factor,
4188                                            target: i as u32,
4189                                        },
4190                                    );
4191                                }
4192                            }
4193                        }
4194                    }
4195
4196                    break 'error None;
4197                };
4198                if let Some(e) = error {
4199                    return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
4200                }
4201            }
4202        }
4203
4204        validation::validate_color_attachment_bytes_per_sample(
4205            color_targets.iter().flatten().map(|cs| cs.format),
4206            self.limits.max_color_attachment_bytes_per_sample,
4207        )
4208        .map_err(pipeline::CreateRenderPipelineError::ColorAttachment)?;
4209
4210        if let Some(ds) = depth_stencil_state {
4211            // See <https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpudepthstencilstate>.
4212            target_specified = true;
4213            let error = 'error: {
4214                if !ds.format.is_depth_stencil_format() {
4215                    // This error case is not redundant with the aspect check below when
4216                    // neither depth nor stencil is enabled at all.
4217                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepthOrStencil(
4218                        ds.format,
4219                    ));
4220                }
4221
4222                let format_features = self.describe_format_features(ds.format)?;
4223                if !format_features
4224                    .allowed_usages
4225                    .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4226                {
4227                    break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(
4228                        ds.format,
4229                    ));
4230                }
4231
4232                let aspect = hal::FormatAspects::from(ds.format);
4233                if aspect.contains(hal::FormatAspects::DEPTH) {
4234                    has_depth_attachment = true;
4235                } else if ds.is_depth_enabled() {
4236                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
4237                }
4238                if has_depth_attachment {
4239                    let Some(depth_write_enabled) = ds.depth_write_enabled else {
4240                        break 'error Some(
4241                            pipeline::DepthStencilStateError::MissingDepthWriteEnabled(ds.format),
4242                        );
4243                    };
4244
4245                    let depth_compare_required = depth_write_enabled
4246                        || ds.stencil.front.depth_fail_op != wgt::StencilOperation::Keep
4247                        || ds.stencil.back.depth_fail_op != wgt::StencilOperation::Keep;
4248                    if depth_compare_required && ds.depth_compare.is_none() {
4249                        break 'error Some(pipeline::DepthStencilStateError::MissingDepthCompare(
4250                            ds.format,
4251                        ));
4252                    }
4253                }
4254
4255                if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
4256                    break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(
4257                        ds.format,
4258                    ));
4259                }
4260                if desc.multisample.count > 1
4261                    && !format_features
4262                        .flags
4263                        .sample_count_supported(desc.multisample.count)
4264                {
4265                    break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(
4266                        desc.multisample.count,
4267                        ds.format,
4268                        ds.format
4269                            .guaranteed_format_features(self.features)
4270                            .flags
4271                            .supported_sample_counts(),
4272                        self.adapter
4273                            .get_texture_format_features(ds.format)
4274                            .flags
4275                            .supported_sample_counts(),
4276                    ));
4277                }
4278
4279                break 'error None;
4280            };
4281            if let Some(e) = error {
4282                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
4283            }
4284
4285            if ds.bias.clamp != 0.0 {
4286                self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
4287            }
4288
4289            if (ds.bias.is_enabled() || ds.bias.clamp != 0.0)
4290                && !desc.primitive.topology.is_triangles()
4291            {
4292                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(
4293                    pipeline::DepthStencilStateError::DepthBiasWithIncompatibleTopology(
4294                        desc.primitive.topology,
4295                    ),
4296                ));
4297            }
4298        }
4299
4300        if !target_specified {
4301            return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);
4302        }
4303
4304        let is_auto_layout = desc.layout.is_none();
4305
4306        // Get the pipeline layout from the desc if it is provided.
4307        let pipeline_layout = match desc.layout {
4308            Some(pipeline_layout) => {
4309                pipeline_layout.same_device(self)?;
4310                Some(pipeline_layout)
4311            }
4312            None => None,
4313        };
4314
4315        let mut binding_layout_source = match pipeline_layout {
4316            Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4317            None => validation::BindingLayoutSource::new_derived(&self.limits),
4318        };
4319
4320        let samples = {
4321            let sc = desc.multisample.count;
4322            if sc == 0 || sc > 32 || !sc.is_power_of_two() {
4323                return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
4324            }
4325            sc
4326        };
4327
4328        let mut vertex_stage = None;
4329        let mut task_stage = None;
4330        let mut mesh_stage = None;
4331        let mut _vertex_entry_point_name = String::new();
4332        let mut _task_entry_point_name = String::new();
4333        let mut _mesh_entry_point_name = String::new();
4334        match desc.vertex {
4335            pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) => {
4336                vertex_stage = {
4337                    let stage_desc = &vertex.stage;
4338                    let stage = validation::ShaderStageForValidation::Vertex {
4339                        topology: desc.primitive.topology,
4340                        compare_function: desc.depth_stencil.as_ref().and_then(|d| d.depth_compare),
4341                    };
4342                    let stage_bit = stage.to_wgt_bit();
4343
4344                    let vertex_shader_module = &stage_desc.module;
4345                    vertex_shader_module.same_device(self)?;
4346
4347                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4348                        stage: stage_bit,
4349                        error,
4350                    };
4351
4352                    _vertex_entry_point_name = vertex_shader_module
4353                        .finalize_entry_point_name(
4354                            stage.to_naga(),
4355                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4356                        )
4357                        .map_err(stage_err)?;
4358
4359                    if let Some(ref interface) = vertex_shader_module.interface {
4360                        io = interface
4361                            .check_stage(
4362                                &mut binding_layout_source,
4363                                &mut shader_binding_sizes,
4364                                &_vertex_entry_point_name,
4365                                stage,
4366                                io,
4367                            )
4368                            .map_err(stage_err)?;
4369                        validated_stages |= stage_bit;
4370                    }
4371                    Some(hal::ProgrammableStage {
4372                        module: vertex_shader_module.raw(),
4373                        entry_point: &_vertex_entry_point_name,
4374                        constants: &stage_desc.constants,
4375                        zero_initialize_workgroup_memory: stage_desc
4376                            .zero_initialize_workgroup_memory,
4377                    })
4378                };
4379            }
4380            pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {
4381                self.require_features(wgt::Features::EXPERIMENTAL_MESH_SHADER)?;
4382
4383                task_stage = if let Some(task) = task {
4384                    let stage_desc = &task.stage;
4385                    let stage = validation::ShaderStageForValidation::Task;
4386                    let stage_bit = stage.to_wgt_bit();
4387                    let task_shader_module = &stage_desc.module;
4388                    task_shader_module.same_device(self)?;
4389
4390                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4391                        stage: stage_bit,
4392                        error,
4393                    };
4394
4395                    _task_entry_point_name = task_shader_module
4396                        .finalize_entry_point_name(
4397                            stage.to_naga(),
4398                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4399                        )
4400                        .map_err(stage_err)?;
4401
4402                    if let Some(ref interface) = task_shader_module.interface {
4403                        io = interface
4404                            .check_stage(
4405                                &mut binding_layout_source,
4406                                &mut shader_binding_sizes,
4407                                &_task_entry_point_name,
4408                                stage,
4409                                io,
4410                            )
4411                            .map_err(stage_err)?;
4412                        validated_stages |= stage_bit;
4413                    }
4414                    Some(hal::ProgrammableStage {
4415                        module: task_shader_module.raw(),
4416                        entry_point: &_task_entry_point_name,
4417                        constants: &stage_desc.constants,
4418                        zero_initialize_workgroup_memory: stage_desc
4419                            .zero_initialize_workgroup_memory,
4420                    })
4421                } else {
4422                    None
4423                };
4424                mesh_stage = {
4425                    let stage_desc = &mesh.stage;
4426                    let stage = validation::ShaderStageForValidation::Mesh;
4427                    let stage_bit = stage.to_wgt_bit();
4428                    let mesh_shader_module = &stage_desc.module;
4429                    mesh_shader_module.same_device(self)?;
4430
4431                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4432                        stage: stage_bit,
4433                        error,
4434                    };
4435
4436                    _mesh_entry_point_name = mesh_shader_module
4437                        .finalize_entry_point_name(
4438                            stage.to_naga(),
4439                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4440                        )
4441                        .map_err(stage_err)?;
4442
4443                    if let Some(ref interface) = mesh_shader_module.interface {
4444                        io = interface
4445                            .check_stage(
4446                                &mut binding_layout_source,
4447                                &mut shader_binding_sizes,
4448                                &_mesh_entry_point_name,
4449                                stage,
4450                                io,
4451                            )
4452                            .map_err(stage_err)?;
4453                        validated_stages |= stage_bit;
4454                    }
4455                    Some(hal::ProgrammableStage {
4456                        module: mesh_shader_module.raw(),
4457                        entry_point: &_mesh_entry_point_name,
4458                        constants: &stage_desc.constants,
4459                        zero_initialize_workgroup_memory: stage_desc
4460                            .zero_initialize_workgroup_memory,
4461                    })
4462                };
4463            }
4464        }
4465
4466        let fragment_entry_point_name;
4467        let fragment_stage = match desc.fragment {
4468            Some(ref fragment_state) => {
4469                let stage = validation::ShaderStageForValidation::Fragment {
4470                    dual_source_blending,
4471                    has_depth_attachment,
4472                };
4473                let stage_bit = stage.to_wgt_bit();
4474
4475                let shader_module = &fragment_state.stage.module;
4476                shader_module.same_device(self)?;
4477
4478                let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4479                    stage: stage_bit,
4480                    error,
4481                };
4482
4483                fragment_entry_point_name = shader_module
4484                    .finalize_entry_point_name(
4485                        stage.to_naga(),
4486                        fragment_state
4487                            .stage
4488                            .entry_point
4489                            .as_ref()
4490                            .map(|ep| ep.as_ref()),
4491                    )
4492                    .map_err(stage_err)?;
4493
4494                if let Some(ref interface) = shader_module.interface {
4495                    io = interface
4496                        .check_stage(
4497                            &mut binding_layout_source,
4498                            &mut shader_binding_sizes,
4499                            &fragment_entry_point_name,
4500                            stage,
4501                            io,
4502                        )
4503                        .map_err(stage_err)?;
4504                    validated_stages |= stage_bit;
4505                }
4506
4507                Some(hal::ProgrammableStage {
4508                    module: shader_module.raw(),
4509                    entry_point: &fragment_entry_point_name,
4510                    constants: &fragment_state.stage.constants,
4511                    zero_initialize_workgroup_memory: fragment_state
4512                        .stage
4513                        .zero_initialize_workgroup_memory,
4514                })
4515            }
4516            None => None,
4517        };
4518
4519        if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
4520            for (i, output) in io.varyings.iter() {
4521                match color_targets.get(*i as usize) {
4522                    Some(Some(state)) => {
4523                        validation::check_texture_format(state.format, &output.ty).map_err(
4524                            |pipeline| {
4525                                pipeline::CreateRenderPipelineError::ColorState(
4526                                    *i as u8,
4527                                    pipeline::ColorStateError::IncompatibleFormat {
4528                                        pipeline,
4529                                        shader: output.ty,
4530                                    },
4531                                )
4532                            },
4533                        )?;
4534                    }
4535                    _ => {
4536                        log::debug!(
4537                            "The fragment stage {:?} output @location({}) values are ignored",
4538                            fragment_stage
4539                                .as_ref()
4540                                .map_or("", |stage| stage.entry_point),
4541                            i
4542                        );
4543                    }
4544                }
4545            }
4546        }
4547        let last_stage = match desc.fragment {
4548            Some(_) => wgt::ShaderStages::FRAGMENT,
4549            None => wgt::ShaderStages::VERTEX,
4550        };
4551        if is_auto_layout && !validated_stages.contains(last_stage) {
4552            return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
4553        }
4554
4555        let pipeline_layout = match binding_layout_source {
4556            validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
4557            validation::BindingLayoutSource::Derived(entries) => {
4558                self.create_derived_pipeline_layout(entries)?
4559            }
4560        };
4561
4562        // Multiview is only supported if the feature is enabled
4563        if let Some(mv_mask) = desc.multiview_mask {
4564            self.require_features(wgt::Features::MULTIVIEW)?;
4565            if !(mv_mask.get() + 1).is_power_of_two() {
4566                self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
4567            }
4568        }
4569
4570        if !self
4571            .downlevel
4572            .flags
4573            .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
4574        {
4575            for (binding, size) in shader_binding_sizes.iter() {
4576                if size.get() % 16 != 0 {
4577                    return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
4578                        binding: binding.binding,
4579                        group: binding.group,
4580                        size: size.get(),
4581                    });
4582                }
4583            }
4584        }
4585
4586        let late_sized_buffer_groups =
4587            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
4588
4589        let cache = match desc.cache {
4590            Some(cache) => {
4591                cache.same_device(self)?;
4592                Some(cache)
4593            }
4594            None => None,
4595        };
4596
4597        let is_mesh = mesh_stage.is_some();
4598        let raw = {
4599            let pipeline_desc = hal::RenderPipelineDescriptor {
4600                label: desc.label.to_hal(self.instance_flags),
4601                layout: pipeline_layout.raw(),
4602                vertex_processor: match vertex_stage {
4603                    Some(vertex_stage) => hal::VertexProcessor::Standard {
4604                        vertex_buffers: &vertex_buffers,
4605                        vertex_stage,
4606                    },
4607                    None => hal::VertexProcessor::Mesh {
4608                        task_stage,
4609                        mesh_stage: mesh_stage.unwrap(),
4610                    },
4611                },
4612                primitive: desc.primitive,
4613                depth_stencil: desc.depth_stencil.clone(),
4614                multisample: desc.multisample,
4615                fragment_stage,
4616                color_targets,
4617                multiview_mask: desc.multiview_mask,
4618                cache: cache.as_ref().map(|it| it.raw()),
4619            };
4620            unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
4621                |err| match err {
4622                    hal::PipelineError::Device(error) => {
4623                        pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))
4624                    }
4625                    hal::PipelineError::Linkage(stage, msg) => {
4626                        pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
4627                    }
4628                    hal::PipelineError::EntryPoint(stage) => {
4629                        pipeline::CreateRenderPipelineError::Internal {
4630                            stage: hal::auxil::map_naga_stage(stage),
4631                            error: ENTRYPOINT_FAILURE_ERROR.to_string(),
4632                        }
4633                    }
4634                    hal::PipelineError::PipelineConstants(stage, error) => {
4635                        pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }
4636                    }
4637                },
4638            )?
4639        };
4640
4641        let pass_context = RenderPassContext {
4642            attachments: AttachmentData {
4643                colors: color_targets
4644                    .iter()
4645                    .map(|state| state.as_ref().map(|s| s.format))
4646                    .collect(),
4647                resolves: ArrayVec::new(),
4648                depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
4649            },
4650            sample_count: samples,
4651            multiview_mask: desc.multiview_mask,
4652        };
4653
4654        let mut flags = pipeline::PipelineFlags::empty();
4655        for state in color_targets.iter().filter_map(|s| s.as_ref()) {
4656            if let Some(ref bs) = state.blend {
4657                if bs.color.uses_constant() | bs.alpha.uses_constant() {
4658                    flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
4659                }
4660            }
4661        }
4662        if let Some(ds) = depth_stencil_state.as_ref() {
4663            if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
4664                flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
4665            }
4666            if !ds.is_depth_read_only() {
4667                flags |= pipeline::PipelineFlags::WRITES_DEPTH;
4668            }
4669            if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
4670                flags |= pipeline::PipelineFlags::WRITES_STENCIL;
4671            }
4672        }
4673        let shader_modules = {
4674            let mut shader_modules = ArrayVec::new();
4675            match desc.vertex {
4676                pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
4677                    shader_modules.push(vertex.stage.module)
4678                }
4679                pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
4680                    if let Some(task) = task {
4681                        shader_modules.push(task.stage.module);
4682                    }
4683                    shader_modules.push(mesh.stage.module);
4684                }
4685            }
4686            shader_modules.extend(desc.fragment.map(|f| f.stage.module));
4687            shader_modules
4688        };
4689
4690        let pipeline = pipeline::RenderPipeline {
4691            raw: ManuallyDrop::new(raw),
4692            layout: pipeline_layout,
4693            device: self.clone(),
4694            pass_context,
4695            _shader_modules: shader_modules,
4696            flags,
4697            topology: desc.primitive.topology,
4698            strip_index_format: desc.primitive.strip_index_format,
4699            vertex_steps,
4700            late_sized_buffer_groups,
4701            label: desc.label.to_string(),
4702            tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),
4703            is_mesh,
4704        };
4705
4706        let pipeline = Arc::new(pipeline);
4707
4708        if is_auto_layout {
4709            for bgl in pipeline.layout.bind_group_layouts.iter() {
4710                let Some(bgl) = bgl else {
4711                    continue;
4712                };
4713
4714                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
4715                let _ = bgl
4716                    .exclusive_pipeline
4717                    .set(binding_model::ExclusivePipeline::Render(Arc::downgrade(
4718                        &pipeline,
4719                    )));
4720            }
4721        }
4722
4723        Ok(pipeline)
4724    }
4725
4726    /// # Safety
4727    /// The `data` field on `desc` must have previously been returned from [`crate::global::Global::pipeline_cache_get_data`]
4728    pub unsafe fn create_pipeline_cache(
4729        self: &Arc<Self>,
4730        desc: &pipeline::PipelineCacheDescriptor,
4731    ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {
4732        use crate::pipeline_cache;
4733
4734        self.check_is_valid()?;
4735
4736        self.require_features(wgt::Features::PIPELINE_CACHE)?;
4737        let data = if let Some((data, validation_key)) = desc
4738            .data
4739            .as_ref()
4740            .zip(self.raw().pipeline_cache_validation_key())
4741        {
4742            let data = pipeline_cache::validate_pipeline_cache(
4743                data,
4744                &self.adapter.raw.info,
4745                validation_key,
4746            );
4747            match data {
4748                Ok(data) => Some(data),
4749                Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),
4750                // If the error was unavoidable and we are asked to fallback, do so
4751                Err(_) => None,
4752            }
4753        } else {
4754            None
4755        };
4756        let cache_desc = hal::PipelineCacheDescriptor {
4757            data,
4758            label: desc.label.to_hal(self.instance_flags),
4759        };
4760        let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {
4761            Ok(raw) => raw,
4762            Err(e) => match e {
4763                hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),
4764            },
4765        };
4766        let cache = pipeline::PipelineCache {
4767            device: self.clone(),
4768            label: desc.label.to_string(),
4769            // This would be none in the error condition, which we don't implement yet
4770            raw: ManuallyDrop::new(raw),
4771        };
4772
4773        let cache = Arc::new(cache);
4774
4775        Ok(cache)
4776    }
4777
4778    fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {
4779        // Variant of adapter.get_texture_format_features that takes device features into account
4780        use wgt::TextureFormatFeatureFlags as tfsc;
4781        let mut format_features = self.adapter.get_texture_format_features(format);
4782        if (format == TextureFormat::R32Float
4783            || format == TextureFormat::Rg32Float
4784            || format == TextureFormat::Rgba32Float)
4785            && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
4786        {
4787            format_features.flags.set(tfsc::FILTERABLE, false);
4788        }
4789        format_features
4790    }
4791
4792    fn describe_format_features(
4793        &self,
4794        format: TextureFormat,
4795    ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
4796        self.require_features(format.required_features())?;
4797
4798        let using_device_features = self
4799            .features
4800            .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
4801        // If we're running downlevel, we need to manually ask the backend what
4802        // we can use as we can't trust WebGPU.
4803        let downlevel = !self
4804            .downlevel
4805            .flags
4806            .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
4807
4808        if using_device_features || downlevel {
4809            Ok(self.get_texture_format_features(format))
4810        } else {
4811            Ok(format.guaranteed_format_features(self.features))
4812        }
4813    }
4814
4815    #[cfg(feature = "replay")]
4816    pub(crate) fn wait_for_submit(
4817        &self,
4818        submission_index: crate::SubmissionIndex,
4819    ) -> Result<(), DeviceError> {
4820        let fence = self.fence.read();
4821        let last_done_index = unsafe { self.raw().get_fence_value(fence.as_ref()) }
4822            .map_err(|e| self.handle_hal_error(e))?;
4823        if last_done_index < submission_index {
4824            unsafe { self.raw().wait(fence.as_ref(), submission_index, None) }
4825                .map_err(|e| self.handle_hal_error(e))?;
4826            drop(fence);
4827            if let Some(queue) = self.get_queue() {
4828                let closures = queue.lock_life().triage_submissions(submission_index);
4829                assert!(
4830                    closures.is_empty(),
4831                    "wait_for_submit is not expected to work with closures"
4832                );
4833            }
4834        }
4835        Ok(())
4836    }
4837
4838    pub fn create_query_set(
4839        self: &Arc<Self>,
4840        desc: &resource::QuerySetDescriptor,
4841    ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {
4842        use resource::CreateQuerySetError as Error;
4843
4844        self.check_is_valid()?;
4845
4846        match desc.ty {
4847            wgt::QueryType::Occlusion => {}
4848            wgt::QueryType::Timestamp => {
4849                self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
4850            }
4851            wgt::QueryType::PipelineStatistics(..) => {
4852                self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
4853            }
4854        }
4855
4856        if desc.count == 0 {
4857            return Err(Error::ZeroCount);
4858        }
4859
4860        if desc.count > wgt::QUERY_SET_MAX_QUERIES {
4861            return Err(Error::TooManyQueries {
4862                count: desc.count,
4863                maximum: wgt::QUERY_SET_MAX_QUERIES,
4864            });
4865        }
4866
4867        let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
4868
4869        let raw = unsafe { self.raw().create_query_set(&hal_desc) }
4870            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
4871
4872        let query_set = QuerySet {
4873            raw: ManuallyDrop::new(raw),
4874            device: self.clone(),
4875            label: desc.label.to_string(),
4876            tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),
4877            desc: desc.map_label(|_| ()),
4878        };
4879
4880        let query_set = Arc::new(query_set);
4881
4882        Ok(query_set)
4883    }
4884
4885    pub fn configure_surface(
4886        self: &Arc<Self>,
4887        surface: &crate::instance::Surface,
4888        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
4889    ) -> Option<present::ConfigureSurfaceError> {
4890        use present::ConfigureSurfaceError as E;
4891        profiling::scope!("surface_configure");
4892
4893        fn validate_surface_configuration(
4894            config: &mut hal::SurfaceConfiguration,
4895            caps: &hal::SurfaceCapabilities,
4896            max_texture_dimension_2d: u32,
4897        ) -> Result<(), E> {
4898            let width = config.extent.width;
4899            let height = config.extent.height;
4900
4901            if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
4902                return Err(E::TooLarge {
4903                    width,
4904                    height,
4905                    max_texture_dimension_2d,
4906                });
4907            }
4908
4909            if !caps.present_modes.contains(&config.present_mode) {
4910                // Automatic present mode checks.
4911                //
4912                // The "Automatic" modes are never supported by the backends.
4913                let fallbacks = match config.present_mode {
4914                    wgt::PresentMode::AutoVsync => {
4915                        &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]
4916                    }
4917                    // Always end in FIFO to make sure it's always supported
4918                    wgt::PresentMode::AutoNoVsync => &[
4919                        wgt::PresentMode::Immediate,
4920                        wgt::PresentMode::Mailbox,
4921                        wgt::PresentMode::Fifo,
4922                    ][..],
4923                    _ => {
4924                        return Err(E::UnsupportedPresentMode {
4925                            requested: config.present_mode,
4926                            available: caps.present_modes.clone(),
4927                        });
4928                    }
4929                };
4930
4931                let new_mode = fallbacks
4932                    .iter()
4933                    .copied()
4934                    .find(|fallback| caps.present_modes.contains(fallback))
4935                    .unwrap_or_else(|| {
4936                        unreachable!(
4937                            "Fallback system failed to choose present mode. \
4938                            This is a bug. Mode: {:?}, Options: {:?}",
4939                            config.present_mode, &caps.present_modes
4940                        );
4941                    });
4942
4943                api_log!(
4944                    "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
4945                    config.present_mode
4946                );
4947                config.present_mode = new_mode;
4948            }
4949            if !caps.formats.contains(&config.format) {
4950                return Err(E::UnsupportedFormat {
4951                    requested: config.format,
4952                    available: caps.formats.clone(),
4953                });
4954            }
4955            if !caps
4956                .composite_alpha_modes
4957                .contains(&config.composite_alpha_mode)
4958            {
4959                let new_alpha_mode = 'alpha: {
4960                    // Automatic alpha mode checks.
4961                    let fallbacks = match config.composite_alpha_mode {
4962                        wgt::CompositeAlphaMode::Auto => &[
4963                            wgt::CompositeAlphaMode::Opaque,
4964                            wgt::CompositeAlphaMode::Inherit,
4965                        ][..],
4966                        _ => {
4967                            return Err(E::UnsupportedAlphaMode {
4968                                requested: config.composite_alpha_mode,
4969                                available: caps.composite_alpha_modes.clone(),
4970                            });
4971                        }
4972                    };
4973
4974                    for &fallback in fallbacks {
4975                        if caps.composite_alpha_modes.contains(&fallback) {
4976                            break 'alpha fallback;
4977                        }
4978                    }
4979
4980                    unreachable!(
4981                        "Fallback system failed to choose alpha mode. This is a bug. \
4982                                  AlphaMode: {:?}, Options: {:?}",
4983                        config.composite_alpha_mode, &caps.composite_alpha_modes
4984                    );
4985                };
4986
4987                api_log!(
4988                    "Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
4989                    config.composite_alpha_mode
4990                );
4991                config.composite_alpha_mode = new_alpha_mode;
4992            }
4993            if !caps.usage.contains(config.usage) {
4994                return Err(E::UnsupportedUsage {
4995                    requested: config.usage,
4996                    available: caps.usage,
4997                });
4998            }
4999            if width == 0 || height == 0 {
5000                return Err(E::ZeroArea);
5001            }
5002            Ok(())
5003        }
5004
5005        log::debug!("configuring surface with {config:?}");
5006
5007        let error = 'error: {
5008            // User callbacks must not be called while we are holding locks.
5009            let user_callbacks;
5010            {
5011                if let Err(e) = self.check_is_valid() {
5012                    break 'error e.into();
5013                }
5014
5015                let caps = match surface.get_capabilities(&self.adapter) {
5016                    Ok(caps) => caps,
5017                    Err(_) => break 'error E::UnsupportedQueueFamily,
5018                };
5019
5020                let mut hal_view_formats = Vec::new();
5021                for format in config.view_formats.iter() {
5022                    if *format == config.format {
5023                        continue;
5024                    }
5025                    if !caps.formats.contains(&config.format) {
5026                        break 'error E::UnsupportedFormat {
5027                            requested: config.format,
5028                            available: caps.formats,
5029                        };
5030                    }
5031                    if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
5032                        break 'error E::InvalidViewFormat(*format, config.format);
5033                    }
5034                    hal_view_formats.push(*format);
5035                }
5036
5037                if !hal_view_formats.is_empty() {
5038                    if let Err(missing_flag) =
5039                        self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
5040                    {
5041                        break 'error E::MissingDownlevelFlags(missing_flag);
5042                    }
5043                }
5044
5045                let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
5046                    *caps.maximum_frame_latency.start(),
5047                    *caps.maximum_frame_latency.end(),
5048                );
5049                let mut hal_config = hal::SurfaceConfiguration {
5050                    maximum_frame_latency,
5051                    present_mode: config.present_mode,
5052                    composite_alpha_mode: config.alpha_mode,
5053                    format: config.format,
5054                    extent: wgt::Extent3d {
5055                        width: config.width,
5056                        height: config.height,
5057                        depth_or_array_layers: 1,
5058                    },
5059                    usage: conv::map_texture_usage(
5060                        config.usage,
5061                        hal::FormatAspects::COLOR,
5062                        wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
5063                            | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
5064                            | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
5065                    ),
5066                    view_formats: hal_view_formats,
5067                };
5068
5069                if let Err(error) = validate_surface_configuration(
5070                    &mut hal_config,
5071                    &caps,
5072                    self.limits.max_texture_dimension_2d,
5073                ) {
5074                    break 'error error;
5075                }
5076
5077                // Wait for all work to finish before configuring the surface.
5078                let snatch_guard = self.snatchable_lock.read();
5079                let fence = self.fence.read();
5080
5081                let maintain_result;
5082                (user_callbacks, maintain_result) =
5083                    self.maintain(fence, wgt::PollType::wait_indefinitely(), snatch_guard);
5084
5085                match maintain_result {
5086                    // We're happy
5087                    Ok(wgt::PollStatus::QueueEmpty) => {}
5088                    Ok(wgt::PollStatus::WaitSucceeded) => {
5089                        // After the wait, the queue should be empty. It can only be non-empty
5090                        // if another thread is submitting at the same time.
5091                        break 'error E::GpuWaitTimeout;
5092                    }
5093                    Ok(wgt::PollStatus::Poll) => {
5094                        unreachable!("Cannot get a Poll result from a Wait action.")
5095                    }
5096                    Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
5097                        // On wasm, you cannot actually successfully wait for the surface.
5098                        // However WebGL does not actually require you do this, so ignoring
5099                        // the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363
5100                    }
5101                    Err(e) => {
5102                        break 'error e.into();
5103                    }
5104                }
5105
5106                // All textures must be destroyed before the surface can be re-configured.
5107                if let Some(present) = surface.presentation.lock().take() {
5108                    if present.acquired_texture.is_some() {
5109                        break 'error E::PreviousOutputExists;
5110                    }
5111                }
5112
5113                // TODO: Texture views may still be alive that point to the texture.
5114                // this will allow the user to render to the surface texture, long after
5115                // it has been removed.
5116                //
5117                // https://github.com/gfx-rs/wgpu/issues/4105
5118
5119                let surface_raw = surface.raw(self.backend()).unwrap();
5120                match unsafe { surface_raw.configure(self.raw(), &hal_config) } {
5121                    Ok(()) => (),
5122                    Err(error) => {
5123                        break 'error match error {
5124                            hal::SurfaceError::Outdated | hal::SurfaceError::Lost => {
5125                                E::InvalidSurface
5126                            }
5127                            hal::SurfaceError::Device(error) => {
5128                                E::Device(self.handle_hal_error(error))
5129                            }
5130                            hal::SurfaceError::Other(message) => {
5131                                log::error!("surface configuration failed: {message}");
5132                                E::InvalidSurface
5133                            }
5134                        }
5135                    }
5136                }
5137
5138                let mut presentation = surface.presentation.lock();
5139                *presentation = Some(present::Presentation {
5140                    device: Arc::clone(self),
5141                    config: config.clone(),
5142                    acquired_texture: None,
5143                });
5144            }
5145
5146            user_callbacks.fire();
5147            return None;
5148        };
5149
5150        Some(error)
5151    }
5152
5153    fn lose(&self, message: &str) {
5154        // Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device.
5155
5156        // Mark the device explicitly as invalid. This is checked in various
5157        // places to prevent new work from being submitted.
5158        self.valid.store(false, Ordering::Release);
5159
5160        // 1) Resolve the GPUDevice device.lost promise.
5161        if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
5162            device_lost_closure(DeviceLostReason::Unknown, message.to_string());
5163        }
5164
5165        // 2) Complete any outstanding mapAsync() steps.
5166        // 3) Complete any outstanding onSubmittedWorkDone() steps.
5167
5168        // These parts are passively accomplished by setting valid to false,
5169        // since that will prevent any new work from being added to the queues.
5170        // Future calls to poll_devices will continue to check the work queues
5171        // until they are cleared, and then drop the device.
5172    }
5173
5174    fn release_gpu_resources(&self) {
5175        // This is called when the device is lost, which makes every associated
5176        // resource invalid and unusable. This is an opportunity to release all of
5177        // the underlying gpu resources, even though the objects remain visible to
5178        // the user agent. We purge this memory naturally when resources have been
5179        // moved into the appropriate buckets, so this function just needs to
5180        // initiate movement into those buckets, and it can do that by calling
5181        // "destroy" on all the resources we know about.
5182
5183        // During these iterations, we discard all errors. We don't care!
5184        let trackers = self.trackers.lock();
5185        for buffer in trackers.buffers.used_resources() {
5186            if let Some(buffer) = Weak::upgrade(buffer) {
5187                buffer.destroy();
5188            }
5189        }
5190        for texture in trackers.textures.used_resources() {
5191            if let Some(texture) = Weak::upgrade(texture) {
5192                texture.destroy();
5193            }
5194        }
5195    }
5196
5197    pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {
5198        UsageScope::new_pooled(&self.usage_scopes, &self.tracker_indices)
5199    }
5200
5201    pub fn get_hal_counters(&self) -> wgt::HalCounters {
5202        self.raw().get_internal_counters()
5203    }
5204
5205    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
5206        self.raw().generate_allocator_report()
5207    }
5208}
5209
5210crate::impl_resource_type!(Device);
5211crate::impl_labeled!(Device);
5212crate::impl_storage_item!(Device);