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