wgpu_core/device/
resource.rs

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