Skip to main content

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