wgpu_core/device/
resource.rs

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