wgpu_core/device/
resource.rs

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