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 req_size = match bb.size.map(wgt::BufferSize::new) {
2798            // Requested a non-zero size
2799            Some(non_zero @ Some(_)) => non_zero,
2800            // Requested size not specified
2801            None => None,
2802            // Requested zero size
2803            Some(None) => {
2804                return Err(binding_model::CreateBindGroupError::BindingZeroSize(
2805                    buffer.error_ident(),
2806                ))
2807            }
2808        };
2809        let (bb, bind_size) = buffer.binding(bb.offset, req_size, snatch_guard)?;
2810
2811        if matches!(binding_ty, wgt::BufferBindingType::Storage { .. })
2812            && bind_size % u64::from(wgt::STORAGE_BINDING_SIZE_ALIGNMENT) != 0
2813        {
2814            return Err(Error::UnalignedEffectiveBufferBindingSizeForStorage {
2815                alignment: wgt::STORAGE_BINDING_SIZE_ALIGNMENT,
2816                size: bind_size,
2817            });
2818        }
2819
2820        let bind_end = bb.offset + bind_size;
2821
2822        if bind_size > range_limit as u64 {
2823            return Err(Error::BufferRangeTooLarge {
2824                binding,
2825                given: bind_size as u32,
2826                limit: range_limit,
2827            });
2828        }
2829
2830        // Record binding info for validating dynamic offsets
2831        if dynamic {
2832            dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
2833                binding_idx: binding,
2834                buffer_size: buffer.size,
2835                binding_range: bb.offset..bind_end,
2836                maximum_dynamic_offset: buffer.size - bind_end,
2837                binding_type: binding_ty,
2838            });
2839        }
2840
2841        if let Some(non_zero) = min_size {
2842            let min_size = non_zero.get();
2843            if min_size > bind_size {
2844                return Err(Error::BindingSizeTooSmall {
2845                    buffer: buffer.error_ident(),
2846                    actual: bind_size,
2847                    min: min_size,
2848                });
2849            }
2850        } else {
2851            let late_size = wgt::BufferSize::new(bind_size)
2852                .ok_or_else(|| Error::BindingZeroSize(buffer.error_ident()))?;
2853            late_buffer_binding_sizes.insert(binding, late_size);
2854        }
2855
2856        // This was checked against the device's alignment requirements above,
2857        // which should always be a multiple of `COPY_BUFFER_ALIGNMENT`.
2858        assert_eq!(bb.offset % wgt::COPY_BUFFER_ALIGNMENT, 0);
2859
2860        // `wgpu_hal` only restricts shader access to bound buffer regions with
2861        // a certain resolution. For the sake of lazy initialization, round up
2862        // the size of the bound range to reflect how much of the buffer is
2863        // actually going to be visible to the shader.
2864        let bounds_check_alignment =
2865            binding_model::buffer_binding_type_bounds_check_alignment(&self.alignments, binding_ty);
2866        let visible_size = align_to(bind_size, bounds_check_alignment);
2867
2868        used_buffer_ranges.extend(buffer.initialization_status.read().create_action(
2869            buffer,
2870            bb.offset..bb.offset + visible_size,
2871            MemoryInitKind::NeedsInitializedMemory,
2872        ));
2873
2874        Ok(bb)
2875    }
2876
2877    fn create_sampler_binding<'a>(
2878        &self,
2879        used: &mut BindGroupStates,
2880        binding: u32,
2881        decl: &wgt::BindGroupLayoutEntry,
2882        sampler: &'a Arc<Sampler>,
2883    ) -> Result<&'a dyn hal::DynSampler, binding_model::CreateBindGroupError> {
2884        use crate::binding_model::CreateBindGroupError as Error;
2885
2886        used.samplers.insert_single(sampler.clone());
2887
2888        sampler.same_device(self)?;
2889
2890        match decl.ty {
2891            wgt::BindingType::Sampler(ty) => {
2892                let (allowed_filtering, allowed_comparison) = match ty {
2893                    wgt::SamplerBindingType::Filtering => (None, false),
2894                    wgt::SamplerBindingType::NonFiltering => (Some(false), false),
2895                    wgt::SamplerBindingType::Comparison => (None, true),
2896                };
2897                if let Some(allowed_filtering) = allowed_filtering {
2898                    if allowed_filtering != sampler.filtering {
2899                        return Err(Error::WrongSamplerFiltering {
2900                            binding,
2901                            layout_flt: allowed_filtering,
2902                            sampler_flt: sampler.filtering,
2903                        });
2904                    }
2905                }
2906                if allowed_comparison != sampler.comparison {
2907                    return Err(Error::WrongSamplerComparison {
2908                        binding,
2909                        layout_cmp: allowed_comparison,
2910                        sampler_cmp: sampler.comparison,
2911                    });
2912                }
2913            }
2914            _ => {
2915                return Err(Error::WrongBindingType {
2916                    binding,
2917                    actual: decl.ty,
2918                    expected: "Sampler",
2919                })
2920            }
2921        }
2922
2923        Ok(sampler.raw())
2924    }
2925
2926    fn create_texture_binding<'a>(
2927        &self,
2928        binding: u32,
2929        decl: &wgt::BindGroupLayoutEntry,
2930        view: &'a Arc<TextureView>,
2931        used: &mut BindGroupStates,
2932        used_texture_ranges: &mut Vec<TextureInitTrackerAction>,
2933        snatch_guard: &'a SnatchGuard<'a>,
2934    ) -> Result<hal::TextureBinding<'a, dyn hal::DynTextureView>, binding_model::CreateBindGroupError>
2935    {
2936        view.same_device(self)?;
2937
2938        let internal_use = self.texture_use_parameters(
2939            binding,
2940            decl,
2941            view,
2942            "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
2943        )?;
2944
2945        used.views.insert_single(view.clone(), internal_use);
2946
2947        let texture = &view.parent;
2948
2949        used_texture_ranges.push(TextureInitTrackerAction {
2950            texture: texture.clone(),
2951            range: TextureInitRange {
2952                mip_range: view.desc.range.mip_range(texture.desc.mip_level_count),
2953                layer_range: view
2954                    .desc
2955                    .range
2956                    .layer_range(texture.desc.array_layer_count()),
2957            },
2958            kind: MemoryInitKind::NeedsInitializedMemory,
2959        });
2960
2961        Ok(hal::TextureBinding {
2962            view: view.try_raw(snatch_guard)?,
2963            usage: internal_use,
2964        })
2965    }
2966
2967    fn create_tlas_binding<'a>(
2968        self: &Arc<Self>,
2969        used: &mut BindGroupStates,
2970        binding: u32,
2971        decl: &wgt::BindGroupLayoutEntry,
2972        tlas: &'a Arc<Tlas>,
2973        snatch_guard: &'a SnatchGuard<'a>,
2974    ) -> Result<&'a dyn hal::DynAccelerationStructure, binding_model::CreateBindGroupError> {
2975        use crate::binding_model::CreateBindGroupError as Error;
2976
2977        used.acceleration_structures.insert_single(tlas.clone());
2978
2979        tlas.same_device(self)?;
2980
2981        match decl.ty {
2982            wgt::BindingType::AccelerationStructure { vertex_return } => {
2983                if vertex_return
2984                    && !tlas.flags.contains(
2985                        wgpu_types::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,
2986                    )
2987                {
2988                    return Err(Error::MissingTLASVertexReturn { binding });
2989                }
2990            }
2991            _ => {
2992                return Err(Error::WrongBindingType {
2993                    binding,
2994                    actual: decl.ty,
2995                    expected: "Tlas",
2996                });
2997            }
2998        }
2999
3000        Ok(tlas.try_raw(snatch_guard)?)
3001    }
3002
3003    fn create_external_texture_binding<'a>(
3004        &'a self,
3005        binding: u32,
3006        decl: &wgt::BindGroupLayoutEntry,
3007        external_texture: &'a Arc<ExternalTexture>,
3008        used: &mut BindGroupStates,
3009        snatch_guard: &'a SnatchGuard,
3010    ) -> Result<
3011        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3012        binding_model::CreateBindGroupError,
3013    > {
3014        use crate::binding_model::CreateBindGroupError as Error;
3015
3016        external_texture.same_device(self)?;
3017
3018        used.external_textures
3019            .insert_single(external_texture.clone());
3020
3021        match decl.ty {
3022            wgt::BindingType::ExternalTexture => {}
3023            _ => {
3024                return Err(Error::WrongBindingType {
3025                    binding,
3026                    actual: decl.ty,
3027                    expected: "ExternalTexture",
3028                });
3029            }
3030        }
3031
3032        let planes = (0..3)
3033            .map(|i| {
3034                // We always need 3 bindings. If we have fewer than 3 planes
3035                // just bind plane 0 multiple times. The shader will only
3036                // sample from valid planes anyway.
3037                let plane = external_texture
3038                    .planes
3039                    .get(i)
3040                    .unwrap_or(&external_texture.planes[0]);
3041                let internal_use = wgt::TextureUses::RESOURCE;
3042                used.views.insert_single(plane.clone(), internal_use);
3043                let view = plane.try_raw(snatch_guard)?;
3044                Ok(hal::TextureBinding {
3045                    view,
3046                    usage: internal_use,
3047                })
3048            })
3049            // We can remove this intermediate Vec by using
3050            // array::try_from_fn() above, once it stabilizes.
3051            .collect::<Result<Vec<_>, Error>>()?;
3052        let planes = planes.try_into().unwrap();
3053
3054        used.buffers
3055            .insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);
3056        let params = external_texture.params.binding(0, None, snatch_guard)?.0;
3057
3058        Ok(hal::ExternalTextureBinding { planes, params })
3059    }
3060
3061    fn create_external_texture_binding_from_view<'a>(
3062        &'a self,
3063        binding: u32,
3064        decl: &wgt::BindGroupLayoutEntry,
3065        view: &'a Arc<TextureView>,
3066        used: &mut BindGroupStates,
3067        snatch_guard: &'a SnatchGuard,
3068    ) -> Result<
3069        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3070        binding_model::CreateBindGroupError,
3071    > {
3072        use crate::binding_model::CreateBindGroupError as Error;
3073
3074        view.same_device(self)?;
3075
3076        let internal_use = self.texture_use_parameters(binding, decl, view, "SampledTexture")?;
3077        used.views.insert_single(view.clone(), internal_use);
3078
3079        match decl.ty {
3080            wgt::BindingType::ExternalTexture => {}
3081            _ => {
3082                return Err(Error::WrongBindingType {
3083                    binding,
3084                    actual: decl.ty,
3085                    expected: "ExternalTexture",
3086                });
3087            }
3088        }
3089
3090        // We need 3 bindings, so just repeat the same texture view 3 times.
3091        let planes = [
3092            hal::TextureBinding {
3093                view: view.try_raw(snatch_guard)?,
3094                usage: internal_use,
3095            },
3096            hal::TextureBinding {
3097                view: view.try_raw(snatch_guard)?,
3098                usage: internal_use,
3099            },
3100            hal::TextureBinding {
3101                view: view.try_raw(snatch_guard)?,
3102                usage: internal_use,
3103            },
3104        ];
3105        let params = hal::BufferBinding::new_unchecked(
3106            self.default_external_texture_params_buffer.as_ref(),
3107            0,
3108            None,
3109        );
3110
3111        Ok(hal::ExternalTextureBinding { planes, params })
3112    }
3113
3114    // This function expects the provided bind group layout to be resolved
3115    // (not passing a duplicate) beforehand.
3116    pub fn create_bind_group(
3117        self: &Arc<Self>,
3118        desc: binding_model::ResolvedBindGroupDescriptor,
3119    ) -> Result<Arc<BindGroup>, binding_model::CreateBindGroupError> {
3120        use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};
3121
3122        let layout = desc.layout;
3123
3124        self.check_is_valid()?;
3125        layout.same_device(self)?;
3126
3127        {
3128            // Check that the number of entries in the descriptor matches
3129            // the number of entries in the layout.
3130            let actual = desc.entries.len();
3131            let expected = layout.entries.len();
3132            if actual != expected {
3133                return Err(Error::BindingsNumMismatch { expected, actual });
3134            }
3135        }
3136
3137        // TODO: arrayvec/smallvec, or re-use allocations
3138        // Record binding info for dynamic offset validation
3139        let mut dynamic_binding_info = Vec::new();
3140        // Map of binding -> shader reflected size
3141        //Note: we can't collect into a vector right away because
3142        // it needs to be in BGL iteration order, not BG entry order.
3143        let mut late_buffer_binding_sizes = FastHashMap::default();
3144        // fill out the descriptors
3145        let mut used = BindGroupStates::new();
3146
3147        let mut used_buffer_ranges = Vec::new();
3148        let mut used_texture_ranges = Vec::new();
3149        let mut hal_entries = Vec::with_capacity(desc.entries.len());
3150        let mut hal_buffers = Vec::new();
3151        let mut hal_samplers = Vec::new();
3152        let mut hal_textures = Vec::new();
3153        let mut hal_tlas_s = Vec::new();
3154        let mut hal_external_textures = Vec::new();
3155        let snatch_guard = self.snatchable_lock.read();
3156        for entry in desc.entries.iter() {
3157            let binding = entry.binding;
3158            // Find the corresponding declaration in the layout
3159            let decl = layout
3160                .entries
3161                .get(binding)
3162                .ok_or(Error::MissingBindingDeclaration(binding))?;
3163            let (res_index, count) = match entry.resource {
3164                Br::Buffer(ref bb) => {
3165                    let bb = self.create_buffer_binding(
3166                        bb,
3167                        binding,
3168                        decl,
3169                        &mut used_buffer_ranges,
3170                        &mut dynamic_binding_info,
3171                        &mut late_buffer_binding_sizes,
3172                        &mut used,
3173                        &snatch_guard,
3174                    )?;
3175
3176                    let res_index = hal_buffers.len();
3177                    hal_buffers.push(bb);
3178                    (res_index, 1)
3179                }
3180                Br::BufferArray(ref bindings_array) => {
3181                    let num_bindings = bindings_array.len();
3182                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3183
3184                    let res_index = hal_buffers.len();
3185                    for bb in bindings_array.iter() {
3186                        let bb = self.create_buffer_binding(
3187                            bb,
3188                            binding,
3189                            decl,
3190                            &mut used_buffer_ranges,
3191                            &mut dynamic_binding_info,
3192                            &mut late_buffer_binding_sizes,
3193                            &mut used,
3194                            &snatch_guard,
3195                        )?;
3196                        hal_buffers.push(bb);
3197                    }
3198                    (res_index, num_bindings)
3199                }
3200                Br::Sampler(ref sampler) => {
3201                    let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3202
3203                    let res_index = hal_samplers.len();
3204                    hal_samplers.push(sampler);
3205                    (res_index, 1)
3206                }
3207                Br::SamplerArray(ref samplers) => {
3208                    let num_bindings = samplers.len();
3209                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3210
3211                    let res_index = hal_samplers.len();
3212                    for sampler in samplers.iter() {
3213                        let sampler =
3214                            self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3215
3216                        hal_samplers.push(sampler);
3217                    }
3218
3219                    (res_index, num_bindings)
3220                }
3221                Br::TextureView(ref view) => match decl.ty {
3222                    wgt::BindingType::ExternalTexture => {
3223                        let et = self.create_external_texture_binding_from_view(
3224                            binding,
3225                            decl,
3226                            view,
3227                            &mut used,
3228                            &snatch_guard,
3229                        )?;
3230                        let res_index = hal_external_textures.len();
3231                        hal_external_textures.push(et);
3232                        (res_index, 1)
3233                    }
3234                    _ => {
3235                        let tb = self.create_texture_binding(
3236                            binding,
3237                            decl,
3238                            view,
3239                            &mut used,
3240                            &mut used_texture_ranges,
3241                            &snatch_guard,
3242                        )?;
3243                        let res_index = hal_textures.len();
3244                        hal_textures.push(tb);
3245                        (res_index, 1)
3246                    }
3247                },
3248                Br::TextureViewArray(ref views) => {
3249                    let num_bindings = views.len();
3250                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3251
3252                    let res_index = hal_textures.len();
3253                    for view in views.iter() {
3254                        let tb = self.create_texture_binding(
3255                            binding,
3256                            decl,
3257                            view,
3258                            &mut used,
3259                            &mut used_texture_ranges,
3260                            &snatch_guard,
3261                        )?;
3262
3263                        hal_textures.push(tb);
3264                    }
3265
3266                    (res_index, num_bindings)
3267                }
3268                Br::AccelerationStructure(ref tlas) => {
3269                    let tlas =
3270                        self.create_tlas_binding(&mut used, binding, decl, tlas, &snatch_guard)?;
3271                    let res_index = hal_tlas_s.len();
3272                    hal_tlas_s.push(tlas);
3273                    (res_index, 1)
3274                }
3275                Br::ExternalTexture(ref et) => {
3276                    let et = self.create_external_texture_binding(
3277                        binding,
3278                        decl,
3279                        et,
3280                        &mut used,
3281                        &snatch_guard,
3282                    )?;
3283                    let res_index = hal_external_textures.len();
3284                    hal_external_textures.push(et);
3285                    (res_index, 1)
3286                }
3287            };
3288
3289            hal_entries.push(hal::BindGroupEntry {
3290                binding,
3291                resource_index: res_index as u32,
3292                count: count as u32,
3293            });
3294        }
3295
3296        used.optimize();
3297
3298        hal_entries.sort_by_key(|entry| entry.binding);
3299        for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
3300            if a.binding == b.binding {
3301                return Err(Error::DuplicateBinding(a.binding));
3302            }
3303        }
3304        let hal_desc = hal::BindGroupDescriptor {
3305            label: desc.label.to_hal(self.instance_flags),
3306            layout: layout.raw(),
3307            entries: &hal_entries,
3308            buffers: &hal_buffers,
3309            samplers: &hal_samplers,
3310            textures: &hal_textures,
3311            acceleration_structures: &hal_tlas_s,
3312            external_textures: &hal_external_textures,
3313        };
3314        let raw = unsafe { self.raw().create_bind_group(&hal_desc) }
3315            .map_err(|e| self.handle_hal_error(e))?;
3316
3317        // collect in the order of BGL iteration
3318        let late_buffer_binding_infos = layout
3319            .entries
3320            .indices()
3321            .flat_map(|binding| {
3322                let size = late_buffer_binding_sizes.get(&binding).cloned()?;
3323                Some(BindGroupLateBufferBindingInfo {
3324                    binding_index: binding,
3325                    size,
3326                })
3327            })
3328            .collect();
3329
3330        let bind_group = BindGroup {
3331            raw: Snatchable::new(raw),
3332            device: self.clone(),
3333            layout,
3334            label: desc.label.to_string(),
3335            tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),
3336            used,
3337            used_buffer_ranges,
3338            used_texture_ranges,
3339            dynamic_binding_info,
3340            late_buffer_binding_infos,
3341        };
3342
3343        let bind_group = Arc::new(bind_group);
3344
3345        let weak_ref = Arc::downgrade(&bind_group);
3346        for range in &bind_group.used_texture_ranges {
3347            let mut bind_groups = range.texture.bind_groups.lock();
3348            bind_groups.push(weak_ref.clone());
3349        }
3350        for range in &bind_group.used_buffer_ranges {
3351            let mut bind_groups = range.buffer.bind_groups.lock();
3352            bind_groups.push(weak_ref.clone());
3353        }
3354
3355        Ok(bind_group)
3356    }
3357
3358    fn check_array_binding(
3359        features: wgt::Features,
3360        count: Option<NonZeroU32>,
3361        num_bindings: usize,
3362    ) -> Result<(), binding_model::CreateBindGroupError> {
3363        use super::binding_model::CreateBindGroupError as Error;
3364
3365        if let Some(count) = count {
3366            let count = count.get() as usize;
3367            if count < num_bindings {
3368                return Err(Error::BindingArrayPartialLengthMismatch {
3369                    actual: num_bindings,
3370                    expected: count,
3371                });
3372            }
3373            if count != num_bindings
3374                && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
3375            {
3376                return Err(Error::BindingArrayLengthMismatch {
3377                    actual: num_bindings,
3378                    expected: count,
3379                });
3380            }
3381            if num_bindings == 0 {
3382                return Err(Error::BindingArrayZeroLength);
3383            }
3384        } else {
3385            return Err(Error::SingleBindingExpected);
3386        };
3387
3388        Ok(())
3389    }
3390
3391    fn texture_use_parameters(
3392        &self,
3393        binding: u32,
3394        decl: &wgt::BindGroupLayoutEntry,
3395        view: &TextureView,
3396        expected: &'static str,
3397    ) -> Result<wgt::TextureUses, binding_model::CreateBindGroupError> {
3398        use crate::binding_model::CreateBindGroupError as Error;
3399        if view
3400            .desc
3401            .aspects()
3402            .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
3403        {
3404            return Err(Error::DepthStencilAspect);
3405        }
3406        match decl.ty {
3407            wgt::BindingType::Texture {
3408                sample_type,
3409                view_dimension,
3410                multisampled,
3411            } => {
3412                use wgt::TextureSampleType as Tst;
3413                if multisampled != (view.samples != 1) {
3414                    return Err(Error::InvalidTextureMultisample {
3415                        binding,
3416                        layout_multisampled: multisampled,
3417                        view_samples: view.samples,
3418                    });
3419                }
3420                let compat_sample_type = view
3421                    .desc
3422                    .format
3423                    .sample_type(Some(view.desc.range.aspect), Some(self.features))
3424                    .unwrap();
3425                match (sample_type, compat_sample_type) {
3426                    (Tst::Uint, Tst::Uint) |
3427                        (Tst::Sint, Tst::Sint) |
3428                        (Tst::Depth, Tst::Depth) |
3429                        // if we expect non-filterable, accept anything float
3430                        (Tst::Float { filterable: false }, Tst::Float { .. }) |
3431                        // if we expect filterable, require it
3432                        (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
3433                        // if we expect non-filterable, also accept depth
3434                        (Tst::Float { filterable: false }, Tst::Depth) => {}
3435                    // if we expect filterable, also accept Float that is defined as
3436                    // unfilterable if filterable feature is explicitly enabled (only hit
3437                    // if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is
3438                    // enabled)
3439                    (Tst::Float { filterable: true }, Tst::Float { .. }) if view.format_features.flags.contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
3440                    _ => {
3441                        return Err(Error::InvalidTextureSampleType {
3442                            binding,
3443                            layout_sample_type: sample_type,
3444                            view_format: view.desc.format,
3445                            view_sample_type: compat_sample_type,
3446                        })
3447                    }
3448                }
3449                if view_dimension != view.desc.dimension {
3450                    return Err(Error::InvalidTextureDimension {
3451                        binding,
3452                        layout_dimension: view_dimension,
3453                        view_dimension: view.desc.dimension,
3454                    });
3455                }
3456                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3457                Ok(wgt::TextureUses::RESOURCE)
3458            }
3459            wgt::BindingType::StorageTexture {
3460                access,
3461                format,
3462                view_dimension,
3463            } => {
3464                if format != view.desc.format {
3465                    return Err(Error::InvalidStorageTextureFormat {
3466                        binding,
3467                        layout_format: format,
3468                        view_format: view.desc.format,
3469                    });
3470                }
3471                if view_dimension != view.desc.dimension {
3472                    return Err(Error::InvalidTextureDimension {
3473                        binding,
3474                        layout_dimension: view_dimension,
3475                        view_dimension: view.desc.dimension,
3476                    });
3477                }
3478
3479                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3480                if mip_level_count != 1 {
3481                    return Err(Error::InvalidStorageTextureMipLevelCount {
3482                        binding,
3483                        mip_level_count,
3484                    });
3485                }
3486
3487                let internal_use = match access {
3488                    wgt::StorageTextureAccess::WriteOnly => {
3489                        if !view
3490                            .format_features
3491                            .flags
3492                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY)
3493                        {
3494                            return Err(Error::StorageWriteNotSupported(view.desc.format));
3495                        }
3496                        wgt::TextureUses::STORAGE_WRITE_ONLY
3497                    }
3498                    wgt::StorageTextureAccess::ReadOnly => {
3499                        if !view
3500                            .format_features
3501                            .flags
3502                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY)
3503                        {
3504                            return Err(Error::StorageReadNotSupported(view.desc.format));
3505                        }
3506                        wgt::TextureUses::STORAGE_READ_ONLY
3507                    }
3508                    wgt::StorageTextureAccess::ReadWrite => {
3509                        if !view
3510                            .format_features
3511                            .flags
3512                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE)
3513                        {
3514                            return Err(Error::StorageReadWriteNotSupported(view.desc.format));
3515                        }
3516
3517                        wgt::TextureUses::STORAGE_READ_WRITE
3518                    }
3519                    wgt::StorageTextureAccess::Atomic => {
3520                        if !view
3521                            .format_features
3522                            .flags
3523                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC)
3524                        {
3525                            return Err(Error::StorageAtomicNotSupported(view.desc.format));
3526                        }
3527
3528                        wgt::TextureUses::STORAGE_ATOMIC
3529                    }
3530                };
3531                view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;
3532                Ok(internal_use)
3533            }
3534            wgt::BindingType::ExternalTexture => {
3535                if view.desc.dimension != TextureViewDimension::D2 {
3536                    return Err(Error::InvalidTextureDimension {
3537                        binding,
3538                        layout_dimension: TextureViewDimension::D2,
3539                        view_dimension: view.desc.dimension,
3540                    });
3541                }
3542                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3543                if mip_level_count != 1 {
3544                    return Err(Error::InvalidExternalTextureMipLevelCount {
3545                        binding,
3546                        mip_level_count,
3547                    });
3548                }
3549                if view.desc.format != TextureFormat::Rgba8Unorm
3550                    && view.desc.format != TextureFormat::Bgra8Unorm
3551                    && view.desc.format != TextureFormat::Rgba16Float
3552                {
3553                    return Err(Error::InvalidExternalTextureFormat {
3554                        binding,
3555                        format: view.desc.format,
3556                    });
3557                }
3558                if view.samples != 1 {
3559                    return Err(Error::InvalidTextureMultisample {
3560                        binding,
3561                        layout_multisampled: false,
3562                        view_samples: view.samples,
3563                    });
3564                }
3565
3566                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3567                Ok(wgt::TextureUses::RESOURCE)
3568            }
3569            _ => Err(Error::WrongBindingType {
3570                binding,
3571                actual: decl.ty,
3572                expected,
3573            }),
3574        }
3575    }
3576
3577    pub fn create_pipeline_layout(
3578        self: &Arc<Self>,
3579        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3580    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
3581        use crate::binding_model::CreatePipelineLayoutError as Error;
3582
3583        self.check_is_valid()?;
3584
3585        let bind_group_layouts_count = desc.bind_group_layouts.len();
3586        let device_max_bind_groups = self.limits.max_bind_groups as usize;
3587        if bind_group_layouts_count > device_max_bind_groups {
3588            return Err(Error::TooManyGroups {
3589                actual: bind_group_layouts_count,
3590                max: device_max_bind_groups,
3591            });
3592        }
3593
3594        if desc.immediate_size != 0 {
3595            self.require_features(wgt::Features::IMMEDIATES)?;
3596        }
3597        if self.limits.max_immediate_size < desc.immediate_size {
3598            return Err(Error::ImmediateRangeTooLarge {
3599                size: desc.immediate_size,
3600                max: self.limits.max_immediate_size,
3601            });
3602        }
3603        if desc.immediate_size % wgt::IMMEDIATE_DATA_ALIGNMENT != 0 {
3604            return Err(Error::MisalignedImmediateSize {
3605                size: desc.immediate_size,
3606            });
3607        }
3608
3609        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
3610
3611        for bgl in desc.bind_group_layouts.iter() {
3612            bgl.same_device(self)?;
3613            count_validator.merge(&bgl.binding_count_validator);
3614        }
3615
3616        count_validator
3617            .validate(&self.limits)
3618            .map_err(Error::TooManyBindings)?;
3619
3620        let bind_group_layouts = desc
3621            .bind_group_layouts
3622            .iter()
3623            .cloned()
3624            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3625
3626        let raw_bind_group_layouts = desc
3627            .bind_group_layouts
3628            .iter()
3629            .map(|bgl| bgl.raw())
3630            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3631
3632        let additional_flags = if self.indirect_validation.is_some() {
3633            hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE
3634        } else {
3635            hal::PipelineLayoutFlags::empty()
3636        };
3637
3638        let hal_desc = hal::PipelineLayoutDescriptor {
3639            label: desc.label.to_hal(self.instance_flags),
3640            flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
3641                | hal::PipelineLayoutFlags::NUM_WORK_GROUPS
3642                | additional_flags,
3643            bind_group_layouts: &raw_bind_group_layouts,
3644            immediate_size: desc.immediate_size,
3645        };
3646
3647        let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }
3648            .map_err(|e| self.handle_hal_error(e))?;
3649
3650        drop(raw_bind_group_layouts);
3651
3652        let layout = binding_model::PipelineLayout {
3653            raw: ManuallyDrop::new(raw),
3654            device: self.clone(),
3655            label: desc.label.to_string(),
3656            bind_group_layouts,
3657            immediate_size: desc.immediate_size,
3658        };
3659
3660        let layout = Arc::new(layout);
3661
3662        Ok(layout)
3663    }
3664
3665    pub(crate) fn derive_pipeline_layout(
3666        self: &Arc<Self>,
3667        mut derived_group_layouts: Box<ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>>,
3668    ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {
3669        while derived_group_layouts
3670            .last()
3671            .is_some_and(|map| map.is_empty())
3672        {
3673            derived_group_layouts.pop();
3674        }
3675
3676        let mut unique_bind_group_layouts = FastHashMap::default();
3677
3678        let bind_group_layouts = derived_group_layouts
3679            .into_iter()
3680            .map(|mut bgl_entry_map| {
3681                bgl_entry_map.sort();
3682                match unique_bind_group_layouts.entry(bgl_entry_map) {
3683                    hashbrown::hash_map::Entry::Occupied(v) => Ok(Arc::clone(v.get())),
3684                    hashbrown::hash_map::Entry::Vacant(e) => {
3685                        match self.create_bind_group_layout_internal(
3686                            &None,
3687                            e.key().clone(),
3688                            bgl::Origin::Derived,
3689                        ) {
3690                            Ok(bgl) => {
3691                                e.insert(bgl.clone());
3692                                Ok(bgl)
3693                            }
3694                            Err(e) => Err(e),
3695                        }
3696                    }
3697                }
3698            })
3699            .collect::<Result<Vec<_>, _>>()?;
3700
3701        let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {
3702            label: None,
3703            bind_group_layouts: Cow::Owned(bind_group_layouts),
3704            immediate_size: 0, //TODO?
3705        };
3706
3707        let layout = self.create_pipeline_layout(&layout_desc)?;
3708        Ok(layout)
3709    }
3710
3711    pub fn create_compute_pipeline(
3712        self: &Arc<Self>,
3713        desc: pipeline::ResolvedComputePipelineDescriptor,
3714    ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {
3715        self.check_is_valid()?;
3716
3717        self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
3718
3719        let shader_module = desc.stage.module;
3720
3721        shader_module.same_device(self)?;
3722
3723        let is_auto_layout = desc.layout.is_none();
3724
3725        // Get the pipeline layout from the desc if it is provided.
3726        let pipeline_layout = match desc.layout {
3727            Some(pipeline_layout) => {
3728                pipeline_layout.same_device(self)?;
3729                Some(pipeline_layout)
3730            }
3731            None => None,
3732        };
3733
3734        let mut binding_layout_source = match pipeline_layout {
3735            Some(ref pipeline_layout) => {
3736                validation::BindingLayoutSource::Provided(pipeline_layout.get_binding_maps())
3737            }
3738            None => validation::BindingLayoutSource::new_derived(&self.limits),
3739        };
3740        let mut shader_binding_sizes = FastHashMap::default();
3741        let io = validation::StageIo::default();
3742
3743        let final_entry_point_name;
3744
3745        {
3746            let stage = wgt::ShaderStages::COMPUTE;
3747
3748            final_entry_point_name = shader_module.finalize_entry_point_name(
3749                stage,
3750                desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
3751            )?;
3752
3753            if let Some(ref interface) = shader_module.interface {
3754                let _ = interface.check_stage(
3755                    &mut binding_layout_source,
3756                    &mut shader_binding_sizes,
3757                    &final_entry_point_name,
3758                    stage,
3759                    io,
3760                    None,
3761                )?;
3762            }
3763        }
3764
3765        let pipeline_layout = match binding_layout_source {
3766            validation::BindingLayoutSource::Provided(_) => {
3767                drop(binding_layout_source);
3768                pipeline_layout.unwrap()
3769            }
3770            validation::BindingLayoutSource::Derived(entries) => {
3771                self.derive_pipeline_layout(entries)?
3772            }
3773        };
3774
3775        let late_sized_buffer_groups =
3776            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
3777
3778        let cache = match desc.cache {
3779            Some(cache) => {
3780                cache.same_device(self)?;
3781                Some(cache)
3782            }
3783            None => None,
3784        };
3785
3786        let pipeline_desc = hal::ComputePipelineDescriptor {
3787            label: desc.label.to_hal(self.instance_flags),
3788            layout: pipeline_layout.raw(),
3789            stage: hal::ProgrammableStage {
3790                module: shader_module.raw(),
3791                entry_point: final_entry_point_name.as_ref(),
3792                constants: &desc.stage.constants,
3793                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
3794            },
3795            cache: cache.as_ref().map(|it| it.raw()),
3796        };
3797
3798        let raw =
3799            unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(
3800                |err| match err {
3801                    hal::PipelineError::Device(error) => {
3802                        pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))
3803                    }
3804                    hal::PipelineError::Linkage(_stages, msg) => {
3805                        pipeline::CreateComputePipelineError::Internal(msg)
3806                    }
3807                    hal::PipelineError::EntryPoint(_stage) => {
3808                        pipeline::CreateComputePipelineError::Internal(
3809                            ENTRYPOINT_FAILURE_ERROR.to_string(),
3810                        )
3811                    }
3812                    hal::PipelineError::PipelineConstants(_stages, msg) => {
3813                        pipeline::CreateComputePipelineError::PipelineConstants(msg)
3814                    }
3815                },
3816            )?;
3817
3818        let pipeline = pipeline::ComputePipeline {
3819            raw: ManuallyDrop::new(raw),
3820            layout: pipeline_layout,
3821            device: self.clone(),
3822            _shader_module: shader_module,
3823            late_sized_buffer_groups,
3824            label: desc.label.to_string(),
3825            tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),
3826        };
3827
3828        let pipeline = Arc::new(pipeline);
3829
3830        if is_auto_layout {
3831            for bgl in pipeline.layout.bind_group_layouts.iter() {
3832                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
3833                let _ = bgl
3834                    .exclusive_pipeline
3835                    .set(binding_model::ExclusivePipeline::Compute(Arc::downgrade(
3836                        &pipeline,
3837                    )));
3838            }
3839        }
3840
3841        Ok(pipeline)
3842    }
3843
3844    pub fn create_render_pipeline(
3845        self: &Arc<Self>,
3846        desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
3847    ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {
3848        use wgt::TextureFormatFeatureFlags as Tfff;
3849
3850        self.check_is_valid()?;
3851
3852        let mut shader_binding_sizes = FastHashMap::default();
3853
3854        let num_attachments = desc.fragment.as_ref().map(|f| f.targets.len()).unwrap_or(0);
3855        let max_attachments = self.limits.max_color_attachments as usize;
3856        if num_attachments > max_attachments {
3857            return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
3858                command::ColorAttachmentError::TooMany {
3859                    given: num_attachments,
3860                    limit: max_attachments,
3861                },
3862            ));
3863        }
3864
3865        let color_targets = desc
3866            .fragment
3867            .as_ref()
3868            .map_or(&[][..], |fragment| &fragment.targets);
3869        let depth_stencil_state = desc.depth_stencil.as_ref();
3870
3871        {
3872            let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
3873                color_targets.iter().filter_map(|x| x.as_ref()).collect();
3874            if !cts.is_empty() && {
3875                let first = &cts[0];
3876                cts[1..]
3877                    .iter()
3878                    .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
3879            } {
3880                self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
3881            }
3882        }
3883
3884        let mut io = validation::StageIo::default();
3885        let mut validated_stages = wgt::ShaderStages::empty();
3886
3887        let mut vertex_steps;
3888        let mut vertex_buffers;
3889        let mut total_attributes;
3890        let mut shader_expects_dual_source_blending = false;
3891        let mut pipeline_expects_dual_source_blending = false;
3892        if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
3893            vertex_steps = Vec::with_capacity(vertex.buffers.len());
3894            vertex_buffers = Vec::with_capacity(vertex.buffers.len());
3895            total_attributes = 0;
3896            shader_expects_dual_source_blending = false;
3897            pipeline_expects_dual_source_blending = false;
3898            for (i, vb_state) in vertex.buffers.iter().enumerate() {
3899                // https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpuvertexbufferlayout
3900
3901                if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
3902                    return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
3903                        index: i as u32,
3904                        given: vb_state.array_stride as u32,
3905                        limit: self.limits.max_vertex_buffer_array_stride,
3906                    });
3907                }
3908                if vb_state.array_stride % wgt::VERTEX_ALIGNMENT != 0 {
3909                    return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
3910                        index: i as u32,
3911                        stride: vb_state.array_stride,
3912                    });
3913                }
3914
3915                let max_stride = if vb_state.array_stride == 0 {
3916                    self.limits.max_vertex_buffer_array_stride as u64
3917                } else {
3918                    vb_state.array_stride
3919                };
3920                let mut last_stride = 0;
3921                for attribute in vb_state.attributes.iter() {
3922                    let attribute_stride = attribute.offset + attribute.format.size();
3923                    if attribute_stride > max_stride {
3924                        return Err(
3925                            pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge {
3926                                location: attribute.shader_location,
3927                                given: attribute_stride as u32,
3928                                limit: max_stride as u32,
3929                            },
3930                        );
3931                    }
3932
3933                    let required_offset_alignment = attribute.format.size().min(4);
3934                    if attribute.offset % required_offset_alignment != 0 {
3935                        return Err(
3936                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
3937                                location: attribute.shader_location,
3938                                offset: attribute.offset,
3939                            },
3940                        );
3941                    }
3942
3943                    if attribute.shader_location >= self.limits.max_vertex_attributes {
3944                        return Err(
3945                            pipeline::CreateRenderPipelineError::VertexAttributeLocationTooLarge {
3946                                given: attribute.shader_location,
3947                                limit: self.limits.max_vertex_attributes,
3948                            },
3949                        );
3950                    }
3951
3952                    last_stride = last_stride.max(attribute_stride);
3953                }
3954                vertex_steps.push(pipeline::VertexStep {
3955                    stride: vb_state.array_stride,
3956                    last_stride,
3957                    mode: vb_state.step_mode,
3958                });
3959                if vb_state.attributes.is_empty() {
3960                    continue;
3961                }
3962                vertex_buffers.push(hal::VertexBufferLayout {
3963                    array_stride: vb_state.array_stride,
3964                    step_mode: vb_state.step_mode,
3965                    attributes: vb_state.attributes.as_ref(),
3966                });
3967
3968                for attribute in vb_state.attributes.iter() {
3969                    if attribute.offset >= 0x10000000 {
3970                        return Err(
3971                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
3972                                location: attribute.shader_location,
3973                                offset: attribute.offset,
3974                            },
3975                        );
3976                    }
3977
3978                    if let wgt::VertexFormat::Float64
3979                    | wgt::VertexFormat::Float64x2
3980                    | wgt::VertexFormat::Float64x3
3981                    | wgt::VertexFormat::Float64x4 = attribute.format
3982                    {
3983                        self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
3984                    }
3985
3986                    let previous = io.varyings.insert(
3987                        attribute.shader_location,
3988                        validation::InterfaceVar::vertex_attribute(attribute.format),
3989                    );
3990
3991                    if previous.is_some() {
3992                        return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
3993                            attribute.shader_location,
3994                        ));
3995                    }
3996                }
3997                total_attributes += vb_state.attributes.len();
3998            }
3999
4000            if vertex_buffers.len() > self.limits.max_vertex_buffers as usize {
4001                return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
4002                    given: vertex_buffers.len() as u32,
4003                    limit: self.limits.max_vertex_buffers,
4004                });
4005            }
4006            if total_attributes > self.limits.max_vertex_attributes as usize {
4007                return Err(
4008                    pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
4009                        given: total_attributes as u32,
4010                        limit: self.limits.max_vertex_attributes,
4011                    },
4012                );
4013            }
4014        } else {
4015            vertex_steps = Vec::new();
4016            vertex_buffers = Vec::new();
4017        };
4018
4019        if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
4020            return Err(
4021                pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
4022                    strip_index_format: desc.primitive.strip_index_format,
4023                    topology: desc.primitive.topology,
4024                },
4025            );
4026        }
4027
4028        if desc.primitive.unclipped_depth {
4029            self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
4030        }
4031
4032        if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
4033            self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
4034        }
4035        if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
4036            self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
4037        }
4038
4039        if desc.primitive.conservative {
4040            self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
4041        }
4042
4043        if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
4044            return Err(
4045                pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
4046            );
4047        }
4048
4049        let mut target_specified = false;
4050
4051        for (i, cs) in color_targets.iter().enumerate() {
4052            if let Some(cs) = cs.as_ref() {
4053                target_specified = true;
4054                let error = 'error: {
4055                    if cs.write_mask.contains_unknown_bits() {
4056                        break 'error Some(pipeline::ColorStateError::InvalidWriteMask(
4057                            cs.write_mask,
4058                        ));
4059                    }
4060
4061                    let format_features = self.describe_format_features(cs.format)?;
4062                    if !format_features
4063                        .allowed_usages
4064                        .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4065                    {
4066                        break 'error Some(pipeline::ColorStateError::FormatNotRenderable(
4067                            cs.format,
4068                        ));
4069                    }
4070                    let blendable = format_features.flags.contains(Tfff::BLENDABLE);
4071                    let filterable = format_features.flags.contains(Tfff::FILTERABLE);
4072                    let adapter_specific = self
4073                        .features
4074                        .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
4075                    // according to WebGPU specifications the texture needs to be
4076                    // [`TextureFormatFeatureFlags::FILTERABLE`] if blending is set - use
4077                    // [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] to elude
4078                    // this limitation
4079                    if cs.blend.is_some() && (!blendable || (!filterable && !adapter_specific)) {
4080                        break 'error Some(pipeline::ColorStateError::FormatNotBlendable(
4081                            cs.format,
4082                        ));
4083                    }
4084                    if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
4085                        break 'error Some(pipeline::ColorStateError::FormatNotColor(cs.format));
4086                    }
4087
4088                    if desc.multisample.count > 1
4089                        && !format_features
4090                            .flags
4091                            .sample_count_supported(desc.multisample.count)
4092                    {
4093                        break 'error Some(pipeline::ColorStateError::InvalidSampleCount(
4094                            desc.multisample.count,
4095                            cs.format,
4096                            cs.format
4097                                .guaranteed_format_features(self.features)
4098                                .flags
4099                                .supported_sample_counts(),
4100                            self.adapter
4101                                .get_texture_format_features(cs.format)
4102                                .flags
4103                                .supported_sample_counts(),
4104                        ));
4105                    }
4106
4107                    if let Some(blend_mode) = cs.blend {
4108                        for factor in [
4109                            blend_mode.color.src_factor,
4110                            blend_mode.color.dst_factor,
4111                            blend_mode.alpha.src_factor,
4112                            blend_mode.alpha.dst_factor,
4113                        ] {
4114                            if factor.ref_second_blend_source() {
4115                                self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
4116                                if i == 0 {
4117                                    pipeline_expects_dual_source_blending = true;
4118                                    break;
4119                                } else {
4120                                    return Err(pipeline::CreateRenderPipelineError
4121                                        ::BlendFactorOnUnsupportedTarget { factor, target: i as u32 });
4122                                }
4123                            }
4124                        }
4125                    }
4126
4127                    break 'error None;
4128                };
4129                if let Some(e) = error {
4130                    return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
4131                }
4132            }
4133        }
4134
4135        validation::validate_color_attachment_bytes_per_sample(
4136            color_targets.iter().flatten().map(|cs| cs.format),
4137            self.limits.max_color_attachment_bytes_per_sample,
4138        )
4139        .map_err(pipeline::CreateRenderPipelineError::ColorAttachment)?;
4140
4141        if let Some(ds) = depth_stencil_state {
4142            target_specified = true;
4143            let error = 'error: {
4144                let format_features = self.describe_format_features(ds.format)?;
4145                if !format_features
4146                    .allowed_usages
4147                    .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4148                {
4149                    break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(
4150                        ds.format,
4151                    ));
4152                }
4153
4154                let aspect = hal::FormatAspects::from(ds.format);
4155                if ds.is_depth_enabled() && !aspect.contains(hal::FormatAspects::DEPTH) {
4156                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
4157                }
4158                if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
4159                    break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(
4160                        ds.format,
4161                    ));
4162                }
4163                if desc.multisample.count > 1
4164                    && !format_features
4165                        .flags
4166                        .sample_count_supported(desc.multisample.count)
4167                {
4168                    break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(
4169                        desc.multisample.count,
4170                        ds.format,
4171                        ds.format
4172                            .guaranteed_format_features(self.features)
4173                            .flags
4174                            .supported_sample_counts(),
4175                        self.adapter
4176                            .get_texture_format_features(ds.format)
4177                            .flags
4178                            .supported_sample_counts(),
4179                    ));
4180                }
4181
4182                break 'error None;
4183            };
4184            if let Some(e) = error {
4185                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
4186            }
4187
4188            if ds.bias.clamp != 0.0 {
4189                self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
4190            }
4191        }
4192
4193        if !target_specified {
4194            return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);
4195        }
4196
4197        let is_auto_layout = desc.layout.is_none();
4198
4199        // Get the pipeline layout from the desc if it is provided.
4200        let pipeline_layout = match desc.layout {
4201            Some(pipeline_layout) => {
4202                pipeline_layout.same_device(self)?;
4203                Some(pipeline_layout)
4204            }
4205            None => None,
4206        };
4207
4208        let mut binding_layout_source = match pipeline_layout {
4209            Some(ref pipeline_layout) => {
4210                validation::BindingLayoutSource::Provided(pipeline_layout.get_binding_maps())
4211            }
4212            None => validation::BindingLayoutSource::new_derived(&self.limits),
4213        };
4214
4215        let samples = {
4216            let sc = desc.multisample.count;
4217            if sc == 0 || sc > 32 || !sc.is_power_of_two() {
4218                return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
4219            }
4220            sc
4221        };
4222
4223        let mut vertex_stage = None;
4224        let mut task_stage = None;
4225        let mut mesh_stage = None;
4226        let mut _vertex_entry_point_name = String::new();
4227        let mut _task_entry_point_name = String::new();
4228        let mut _mesh_entry_point_name = String::new();
4229        match desc.vertex {
4230            pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) => {
4231                vertex_stage = {
4232                    let stage_desc = &vertex.stage;
4233                    let stage = wgt::ShaderStages::VERTEX;
4234
4235                    let vertex_shader_module = &stage_desc.module;
4236                    vertex_shader_module.same_device(self)?;
4237
4238                    let stage_err =
4239                        |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
4240
4241                    _vertex_entry_point_name = vertex_shader_module
4242                        .finalize_entry_point_name(
4243                            stage,
4244                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4245                        )
4246                        .map_err(stage_err)?;
4247
4248                    if let Some(ref interface) = vertex_shader_module.interface {
4249                        io = interface
4250                            .check_stage(
4251                                &mut binding_layout_source,
4252                                &mut shader_binding_sizes,
4253                                &_vertex_entry_point_name,
4254                                stage,
4255                                io,
4256                                desc.depth_stencil.as_ref().map(|d| d.depth_compare),
4257                            )
4258                            .map_err(stage_err)?;
4259                        validated_stages |= stage;
4260                    }
4261                    Some(hal::ProgrammableStage {
4262                        module: vertex_shader_module.raw(),
4263                        entry_point: &_vertex_entry_point_name,
4264                        constants: &stage_desc.constants,
4265                        zero_initialize_workgroup_memory: stage_desc
4266                            .zero_initialize_workgroup_memory,
4267                    })
4268                };
4269            }
4270            pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {
4271                self.require_features(wgt::Features::EXPERIMENTAL_MESH_SHADER)?;
4272
4273                task_stage = if let Some(task) = task {
4274                    let stage_desc = &task.stage;
4275                    let stage = wgt::ShaderStages::TASK;
4276                    let task_shader_module = &stage_desc.module;
4277                    task_shader_module.same_device(self)?;
4278
4279                    let stage_err =
4280                        |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
4281
4282                    _task_entry_point_name = task_shader_module
4283                        .finalize_entry_point_name(
4284                            stage,
4285                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4286                        )
4287                        .map_err(stage_err)?;
4288
4289                    if let Some(ref interface) = task_shader_module.interface {
4290                        io = interface
4291                            .check_stage(
4292                                &mut binding_layout_source,
4293                                &mut shader_binding_sizes,
4294                                &_task_entry_point_name,
4295                                stage,
4296                                io,
4297                                desc.depth_stencil.as_ref().map(|d| d.depth_compare),
4298                            )
4299                            .map_err(stage_err)?;
4300                        validated_stages |= stage;
4301                    }
4302                    Some(hal::ProgrammableStage {
4303                        module: task_shader_module.raw(),
4304                        entry_point: &_task_entry_point_name,
4305                        constants: &stage_desc.constants,
4306                        zero_initialize_workgroup_memory: stage_desc
4307                            .zero_initialize_workgroup_memory,
4308                    })
4309                } else {
4310                    None
4311                };
4312                mesh_stage = {
4313                    let stage_desc = &mesh.stage;
4314                    let stage = wgt::ShaderStages::MESH;
4315                    let mesh_shader_module = &stage_desc.module;
4316                    mesh_shader_module.same_device(self)?;
4317
4318                    let stage_err =
4319                        |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
4320
4321                    _mesh_entry_point_name = mesh_shader_module
4322                        .finalize_entry_point_name(
4323                            stage,
4324                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4325                        )
4326                        .map_err(stage_err)?;
4327
4328                    if let Some(ref interface) = mesh_shader_module.interface {
4329                        io = interface
4330                            .check_stage(
4331                                &mut binding_layout_source,
4332                                &mut shader_binding_sizes,
4333                                &_mesh_entry_point_name,
4334                                stage,
4335                                io,
4336                                desc.depth_stencil.as_ref().map(|d| d.depth_compare),
4337                            )
4338                            .map_err(stage_err)?;
4339                        validated_stages |= stage;
4340                    }
4341                    Some(hal::ProgrammableStage {
4342                        module: mesh_shader_module.raw(),
4343                        entry_point: &_mesh_entry_point_name,
4344                        constants: &stage_desc.constants,
4345                        zero_initialize_workgroup_memory: stage_desc
4346                            .zero_initialize_workgroup_memory,
4347                    })
4348                };
4349            }
4350        }
4351
4352        let fragment_entry_point_name;
4353        let fragment_stage = match desc.fragment {
4354            Some(ref fragment_state) => {
4355                let stage = wgt::ShaderStages::FRAGMENT;
4356
4357                let shader_module = &fragment_state.stage.module;
4358                shader_module.same_device(self)?;
4359
4360                let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
4361
4362                fragment_entry_point_name = shader_module
4363                    .finalize_entry_point_name(
4364                        stage,
4365                        fragment_state
4366                            .stage
4367                            .entry_point
4368                            .as_ref()
4369                            .map(|ep| ep.as_ref()),
4370                    )
4371                    .map_err(stage_err)?;
4372
4373                if let Some(ref interface) = shader_module.interface {
4374                    io = interface
4375                        .check_stage(
4376                            &mut binding_layout_source,
4377                            &mut shader_binding_sizes,
4378                            &fragment_entry_point_name,
4379                            stage,
4380                            io,
4381                            desc.depth_stencil.as_ref().map(|d| d.depth_compare),
4382                        )
4383                        .map_err(stage_err)?;
4384                    validated_stages |= stage;
4385                }
4386
4387                if let Some(ref interface) = shader_module.interface {
4388                    shader_expects_dual_source_blending = interface
4389                        .fragment_uses_dual_source_blending(&fragment_entry_point_name)
4390                        .map_err(|error| pipeline::CreateRenderPipelineError::Stage {
4391                            stage,
4392                            error,
4393                        })?;
4394                }
4395
4396                Some(hal::ProgrammableStage {
4397                    module: shader_module.raw(),
4398                    entry_point: &fragment_entry_point_name,
4399                    constants: &fragment_state.stage.constants,
4400                    zero_initialize_workgroup_memory: fragment_state
4401                        .stage
4402                        .zero_initialize_workgroup_memory,
4403                })
4404            }
4405            None => None,
4406        };
4407
4408        if !pipeline_expects_dual_source_blending && shader_expects_dual_source_blending {
4409            return Err(
4410                pipeline::CreateRenderPipelineError::ShaderExpectsPipelineToUseDualSourceBlending,
4411            );
4412        }
4413        if pipeline_expects_dual_source_blending && !shader_expects_dual_source_blending {
4414            return Err(
4415                pipeline::CreateRenderPipelineError::PipelineExpectsShaderToUseDualSourceBlending,
4416            );
4417        }
4418
4419        if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
4420            for (i, output) in io.varyings.iter() {
4421                match color_targets.get(*i as usize) {
4422                    Some(Some(state)) => {
4423                        validation::check_texture_format(state.format, &output.ty).map_err(
4424                            |pipeline| {
4425                                pipeline::CreateRenderPipelineError::ColorState(
4426                                    *i as u8,
4427                                    pipeline::ColorStateError::IncompatibleFormat {
4428                                        pipeline,
4429                                        shader: output.ty,
4430                                    },
4431                                )
4432                            },
4433                        )?;
4434                    }
4435                    _ => {
4436                        log::debug!(
4437                            "The fragment stage {:?} output @location({}) values are ignored",
4438                            fragment_stage
4439                                .as_ref()
4440                                .map_or("", |stage| stage.entry_point),
4441                            i
4442                        );
4443                    }
4444                }
4445            }
4446        }
4447        let last_stage = match desc.fragment {
4448            Some(_) => wgt::ShaderStages::FRAGMENT,
4449            None => wgt::ShaderStages::VERTEX,
4450        };
4451        if is_auto_layout && !validated_stages.contains(last_stage) {
4452            return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
4453        }
4454
4455        let pipeline_layout = match binding_layout_source {
4456            validation::BindingLayoutSource::Provided(_) => {
4457                drop(binding_layout_source);
4458                pipeline_layout.unwrap()
4459            }
4460            validation::BindingLayoutSource::Derived(entries) => {
4461                self.derive_pipeline_layout(entries)?
4462            }
4463        };
4464
4465        // Multiview is only supported if the feature is enabled
4466        if let Some(mv_mask) = desc.multiview_mask {
4467            self.require_features(wgt::Features::MULTIVIEW)?;
4468            if !(mv_mask.get() + 1).is_power_of_two() {
4469                self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
4470            }
4471        }
4472
4473        if !self
4474            .downlevel
4475            .flags
4476            .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
4477        {
4478            for (binding, size) in shader_binding_sizes.iter() {
4479                if size.get() % 16 != 0 {
4480                    return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
4481                        binding: binding.binding,
4482                        group: binding.group,
4483                        size: size.get(),
4484                    });
4485                }
4486            }
4487        }
4488
4489        let late_sized_buffer_groups =
4490            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
4491
4492        let cache = match desc.cache {
4493            Some(cache) => {
4494                cache.same_device(self)?;
4495                Some(cache)
4496            }
4497            None => None,
4498        };
4499
4500        let is_mesh = mesh_stage.is_some();
4501        let raw = {
4502            let pipeline_desc = hal::RenderPipelineDescriptor {
4503                label: desc.label.to_hal(self.instance_flags),
4504                layout: pipeline_layout.raw(),
4505                vertex_processor: match vertex_stage {
4506                    Some(vertex_stage) => hal::VertexProcessor::Standard {
4507                        vertex_buffers: &vertex_buffers,
4508                        vertex_stage,
4509                    },
4510                    None => hal::VertexProcessor::Mesh {
4511                        task_stage,
4512                        mesh_stage: mesh_stage.unwrap(),
4513                    },
4514                },
4515                primitive: desc.primitive,
4516                depth_stencil: desc.depth_stencil.clone(),
4517                multisample: desc.multisample,
4518                fragment_stage,
4519                color_targets,
4520                multiview_mask: desc.multiview_mask,
4521                cache: cache.as_ref().map(|it| it.raw()),
4522            };
4523            unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
4524                |err| match err {
4525                    hal::PipelineError::Device(error) => {
4526                        pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))
4527                    }
4528                    hal::PipelineError::Linkage(stage, msg) => {
4529                        pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
4530                    }
4531                    hal::PipelineError::EntryPoint(stage) => {
4532                        pipeline::CreateRenderPipelineError::Internal {
4533                            stage: hal::auxil::map_naga_stage(stage),
4534                            error: ENTRYPOINT_FAILURE_ERROR.to_string(),
4535                        }
4536                    }
4537                    hal::PipelineError::PipelineConstants(stage, error) => {
4538                        pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }
4539                    }
4540                },
4541            )?
4542        };
4543
4544        let pass_context = RenderPassContext {
4545            attachments: AttachmentData {
4546                colors: color_targets
4547                    .iter()
4548                    .map(|state| state.as_ref().map(|s| s.format))
4549                    .collect(),
4550                resolves: ArrayVec::new(),
4551                depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
4552            },
4553            sample_count: samples,
4554            multiview_mask: desc.multiview_mask,
4555        };
4556
4557        let mut flags = pipeline::PipelineFlags::empty();
4558        for state in color_targets.iter().filter_map(|s| s.as_ref()) {
4559            if let Some(ref bs) = state.blend {
4560                if bs.color.uses_constant() | bs.alpha.uses_constant() {
4561                    flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
4562                }
4563            }
4564        }
4565        if let Some(ds) = depth_stencil_state.as_ref() {
4566            if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
4567                flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
4568            }
4569            if !ds.is_depth_read_only() {
4570                flags |= pipeline::PipelineFlags::WRITES_DEPTH;
4571            }
4572            if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
4573                flags |= pipeline::PipelineFlags::WRITES_STENCIL;
4574            }
4575        }
4576        let shader_modules = {
4577            let mut shader_modules = ArrayVec::new();
4578            match desc.vertex {
4579                pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
4580                    shader_modules.push(vertex.stage.module)
4581                }
4582                pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
4583                    if let Some(task) = task {
4584                        shader_modules.push(task.stage.module);
4585                    }
4586                    shader_modules.push(mesh.stage.module);
4587                }
4588            }
4589            shader_modules.extend(desc.fragment.map(|f| f.stage.module));
4590            shader_modules
4591        };
4592
4593        let pipeline = pipeline::RenderPipeline {
4594            raw: ManuallyDrop::new(raw),
4595            layout: pipeline_layout,
4596            device: self.clone(),
4597            pass_context,
4598            _shader_modules: shader_modules,
4599            flags,
4600            strip_index_format: desc.primitive.strip_index_format,
4601            vertex_steps,
4602            late_sized_buffer_groups,
4603            label: desc.label.to_string(),
4604            tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),
4605            is_mesh,
4606        };
4607
4608        let pipeline = Arc::new(pipeline);
4609
4610        if is_auto_layout {
4611            for bgl in pipeline.layout.bind_group_layouts.iter() {
4612                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
4613                let _ = bgl
4614                    .exclusive_pipeline
4615                    .set(binding_model::ExclusivePipeline::Render(Arc::downgrade(
4616                        &pipeline,
4617                    )));
4618            }
4619        }
4620
4621        Ok(pipeline)
4622    }
4623
4624    /// # Safety
4625    /// The `data` field on `desc` must have previously been returned from [`crate::global::Global::pipeline_cache_get_data`]
4626    pub unsafe fn create_pipeline_cache(
4627        self: &Arc<Self>,
4628        desc: &pipeline::PipelineCacheDescriptor,
4629    ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {
4630        use crate::pipeline_cache;
4631
4632        self.check_is_valid()?;
4633
4634        self.require_features(wgt::Features::PIPELINE_CACHE)?;
4635        let data = if let Some((data, validation_key)) = desc
4636            .data
4637            .as_ref()
4638            .zip(self.raw().pipeline_cache_validation_key())
4639        {
4640            let data = pipeline_cache::validate_pipeline_cache(
4641                data,
4642                &self.adapter.raw.info,
4643                validation_key,
4644            );
4645            match data {
4646                Ok(data) => Some(data),
4647                Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),
4648                // If the error was unavoidable and we are asked to fallback, do so
4649                Err(_) => None,
4650            }
4651        } else {
4652            None
4653        };
4654        let cache_desc = hal::PipelineCacheDescriptor {
4655            data,
4656            label: desc.label.to_hal(self.instance_flags),
4657        };
4658        let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {
4659            Ok(raw) => raw,
4660            Err(e) => match e {
4661                hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),
4662            },
4663        };
4664        let cache = pipeline::PipelineCache {
4665            device: self.clone(),
4666            label: desc.label.to_string(),
4667            // This would be none in the error condition, which we don't implement yet
4668            raw: ManuallyDrop::new(raw),
4669        };
4670
4671        let cache = Arc::new(cache);
4672
4673        Ok(cache)
4674    }
4675
4676    fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {
4677        // Variant of adapter.get_texture_format_features that takes device features into account
4678        use wgt::TextureFormatFeatureFlags as tfsc;
4679        let mut format_features = self.adapter.get_texture_format_features(format);
4680        if (format == TextureFormat::R32Float
4681            || format == TextureFormat::Rg32Float
4682            || format == TextureFormat::Rgba32Float)
4683            && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
4684        {
4685            format_features.flags.set(tfsc::FILTERABLE, false);
4686        }
4687        format_features
4688    }
4689
4690    fn describe_format_features(
4691        &self,
4692        format: TextureFormat,
4693    ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
4694        self.require_features(format.required_features())?;
4695
4696        let using_device_features = self
4697            .features
4698            .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
4699        // If we're running downlevel, we need to manually ask the backend what
4700        // we can use as we can't trust WebGPU.
4701        let downlevel = !self
4702            .downlevel
4703            .flags
4704            .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
4705
4706        if using_device_features || downlevel {
4707            Ok(self.get_texture_format_features(format))
4708        } else {
4709            Ok(format.guaranteed_format_features(self.features))
4710        }
4711    }
4712
4713    #[cfg(feature = "replay")]
4714    pub(crate) fn wait_for_submit(
4715        &self,
4716        submission_index: crate::SubmissionIndex,
4717    ) -> Result<(), DeviceError> {
4718        let fence = self.fence.read();
4719        let last_done_index = unsafe { self.raw().get_fence_value(fence.as_ref()) }
4720            .map_err(|e| self.handle_hal_error(e))?;
4721        if last_done_index < submission_index {
4722            unsafe { self.raw().wait(fence.as_ref(), submission_index, None) }
4723                .map_err(|e| self.handle_hal_error(e))?;
4724            drop(fence);
4725            if let Some(queue) = self.get_queue() {
4726                let closures = queue.lock_life().triage_submissions(submission_index);
4727                assert!(
4728                    closures.is_empty(),
4729                    "wait_for_submit is not expected to work with closures"
4730                );
4731            }
4732        }
4733        Ok(())
4734    }
4735
4736    pub fn create_query_set(
4737        self: &Arc<Self>,
4738        desc: &resource::QuerySetDescriptor,
4739    ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {
4740        use resource::CreateQuerySetError as Error;
4741
4742        self.check_is_valid()?;
4743
4744        match desc.ty {
4745            wgt::QueryType::Occlusion => {}
4746            wgt::QueryType::Timestamp => {
4747                self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
4748            }
4749            wgt::QueryType::PipelineStatistics(..) => {
4750                self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
4751            }
4752        }
4753
4754        if desc.count == 0 {
4755            return Err(Error::ZeroCount);
4756        }
4757
4758        if desc.count > wgt::QUERY_SET_MAX_QUERIES {
4759            return Err(Error::TooManyQueries {
4760                count: desc.count,
4761                maximum: wgt::QUERY_SET_MAX_QUERIES,
4762            });
4763        }
4764
4765        let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
4766
4767        let raw = unsafe { self.raw().create_query_set(&hal_desc) }
4768            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
4769
4770        let query_set = QuerySet {
4771            raw: ManuallyDrop::new(raw),
4772            device: self.clone(),
4773            label: desc.label.to_string(),
4774            tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),
4775            desc: desc.map_label(|_| ()),
4776        };
4777
4778        let query_set = Arc::new(query_set);
4779
4780        Ok(query_set)
4781    }
4782
4783    pub fn configure_surface(
4784        self: &Arc<Self>,
4785        surface: &crate::instance::Surface,
4786        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
4787    ) -> Option<present::ConfigureSurfaceError> {
4788        use present::ConfigureSurfaceError as E;
4789        profiling::scope!("surface_configure");
4790
4791        fn validate_surface_configuration(
4792            config: &mut hal::SurfaceConfiguration,
4793            caps: &hal::SurfaceCapabilities,
4794            max_texture_dimension_2d: u32,
4795        ) -> Result<(), E> {
4796            let width = config.extent.width;
4797            let height = config.extent.height;
4798
4799            if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
4800                return Err(E::TooLarge {
4801                    width,
4802                    height,
4803                    max_texture_dimension_2d,
4804                });
4805            }
4806
4807            if !caps.present_modes.contains(&config.present_mode) {
4808                // Automatic present mode checks.
4809                //
4810                // The "Automatic" modes are never supported by the backends.
4811                let fallbacks = match config.present_mode {
4812                    wgt::PresentMode::AutoVsync => {
4813                        &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]
4814                    }
4815                    // Always end in FIFO to make sure it's always supported
4816                    wgt::PresentMode::AutoNoVsync => &[
4817                        wgt::PresentMode::Immediate,
4818                        wgt::PresentMode::Mailbox,
4819                        wgt::PresentMode::Fifo,
4820                    ][..],
4821                    _ => {
4822                        return Err(E::UnsupportedPresentMode {
4823                            requested: config.present_mode,
4824                            available: caps.present_modes.clone(),
4825                        });
4826                    }
4827                };
4828
4829                let new_mode = fallbacks
4830                    .iter()
4831                    .copied()
4832                    .find(|fallback| caps.present_modes.contains(fallback))
4833                    .unwrap_or_else(|| {
4834                        unreachable!(
4835                            "Fallback system failed to choose present mode. \
4836                            This is a bug. Mode: {:?}, Options: {:?}",
4837                            config.present_mode, &caps.present_modes
4838                        );
4839                    });
4840
4841                api_log!(
4842                    "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
4843                    config.present_mode
4844                );
4845                config.present_mode = new_mode;
4846            }
4847            if !caps.formats.contains(&config.format) {
4848                return Err(E::UnsupportedFormat {
4849                    requested: config.format,
4850                    available: caps.formats.clone(),
4851                });
4852            }
4853            if !caps
4854                .composite_alpha_modes
4855                .contains(&config.composite_alpha_mode)
4856            {
4857                let new_alpha_mode = 'alpha: {
4858                    // Automatic alpha mode checks.
4859                    let fallbacks = match config.composite_alpha_mode {
4860                        wgt::CompositeAlphaMode::Auto => &[
4861                            wgt::CompositeAlphaMode::Opaque,
4862                            wgt::CompositeAlphaMode::Inherit,
4863                        ][..],
4864                        _ => {
4865                            return Err(E::UnsupportedAlphaMode {
4866                                requested: config.composite_alpha_mode,
4867                                available: caps.composite_alpha_modes.clone(),
4868                            });
4869                        }
4870                    };
4871
4872                    for &fallback in fallbacks {
4873                        if caps.composite_alpha_modes.contains(&fallback) {
4874                            break 'alpha fallback;
4875                        }
4876                    }
4877
4878                    unreachable!(
4879                        "Fallback system failed to choose alpha mode. This is a bug. \
4880                                  AlphaMode: {:?}, Options: {:?}",
4881                        config.composite_alpha_mode, &caps.composite_alpha_modes
4882                    );
4883                };
4884
4885                api_log!(
4886                    "Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
4887                    config.composite_alpha_mode
4888                );
4889                config.composite_alpha_mode = new_alpha_mode;
4890            }
4891            if !caps.usage.contains(config.usage) {
4892                return Err(E::UnsupportedUsage {
4893                    requested: config.usage,
4894                    available: caps.usage,
4895                });
4896            }
4897            if width == 0 || height == 0 {
4898                return Err(E::ZeroArea);
4899            }
4900            Ok(())
4901        }
4902
4903        log::debug!("configuring surface with {config:?}");
4904
4905        let error = 'error: {
4906            // User callbacks must not be called while we are holding locks.
4907            let user_callbacks;
4908            {
4909                if let Err(e) = self.check_is_valid() {
4910                    break 'error e.into();
4911                }
4912
4913                let caps = match surface.get_capabilities(&self.adapter) {
4914                    Ok(caps) => caps,
4915                    Err(_) => break 'error E::UnsupportedQueueFamily,
4916                };
4917
4918                let mut hal_view_formats = Vec::new();
4919                for format in config.view_formats.iter() {
4920                    if *format == config.format {
4921                        continue;
4922                    }
4923                    if !caps.formats.contains(&config.format) {
4924                        break 'error E::UnsupportedFormat {
4925                            requested: config.format,
4926                            available: caps.formats,
4927                        };
4928                    }
4929                    if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
4930                        break 'error E::InvalidViewFormat(*format, config.format);
4931                    }
4932                    hal_view_formats.push(*format);
4933                }
4934
4935                if !hal_view_formats.is_empty() {
4936                    if let Err(missing_flag) =
4937                        self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
4938                    {
4939                        break 'error E::MissingDownlevelFlags(missing_flag);
4940                    }
4941                }
4942
4943                let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
4944                    *caps.maximum_frame_latency.start(),
4945                    *caps.maximum_frame_latency.end(),
4946                );
4947                let mut hal_config = hal::SurfaceConfiguration {
4948                    maximum_frame_latency,
4949                    present_mode: config.present_mode,
4950                    composite_alpha_mode: config.alpha_mode,
4951                    format: config.format,
4952                    extent: wgt::Extent3d {
4953                        width: config.width,
4954                        height: config.height,
4955                        depth_or_array_layers: 1,
4956                    },
4957                    usage: conv::map_texture_usage(
4958                        config.usage,
4959                        hal::FormatAspects::COLOR,
4960                        wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
4961                            | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
4962                            | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
4963                    ),
4964                    view_formats: hal_view_formats,
4965                };
4966
4967                if let Err(error) = validate_surface_configuration(
4968                    &mut hal_config,
4969                    &caps,
4970                    self.limits.max_texture_dimension_2d,
4971                ) {
4972                    break 'error error;
4973                }
4974
4975                // Wait for all work to finish before configuring the surface.
4976                let snatch_guard = self.snatchable_lock.read();
4977                let fence = self.fence.read();
4978
4979                let maintain_result;
4980                (user_callbacks, maintain_result) =
4981                    self.maintain(fence, wgt::PollType::wait_indefinitely(), snatch_guard);
4982
4983                match maintain_result {
4984                    // We're happy
4985                    Ok(wgt::PollStatus::QueueEmpty) => {}
4986                    Ok(wgt::PollStatus::WaitSucceeded) => {
4987                        // After the wait, the queue should be empty. It can only be non-empty
4988                        // if another thread is submitting at the same time.
4989                        break 'error E::GpuWaitTimeout;
4990                    }
4991                    Ok(wgt::PollStatus::Poll) => {
4992                        unreachable!("Cannot get a Poll result from a Wait action.")
4993                    }
4994                    Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
4995                        // On wasm, you cannot actually successfully wait for the surface.
4996                        // However WebGL does not actually require you do this, so ignoring
4997                        // the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363
4998                    }
4999                    Err(e) => {
5000                        break 'error e.into();
5001                    }
5002                }
5003
5004                // All textures must be destroyed before the surface can be re-configured.
5005                if let Some(present) = surface.presentation.lock().take() {
5006                    if present.acquired_texture.is_some() {
5007                        break 'error E::PreviousOutputExists;
5008                    }
5009                }
5010
5011                // TODO: Texture views may still be alive that point to the texture.
5012                // this will allow the user to render to the surface texture, long after
5013                // it has been removed.
5014                //
5015                // https://github.com/gfx-rs/wgpu/issues/4105
5016
5017                let surface_raw = surface.raw(self.backend()).unwrap();
5018                match unsafe { surface_raw.configure(self.raw(), &hal_config) } {
5019                    Ok(()) => (),
5020                    Err(error) => {
5021                        break 'error match error {
5022                            hal::SurfaceError::Outdated | hal::SurfaceError::Lost => {
5023                                E::InvalidSurface
5024                            }
5025                            hal::SurfaceError::Device(error) => {
5026                                E::Device(self.handle_hal_error(error))
5027                            }
5028                            hal::SurfaceError::Other(message) => {
5029                                log::error!("surface configuration failed: {message}");
5030                                E::InvalidSurface
5031                            }
5032                        }
5033                    }
5034                }
5035
5036                let mut presentation = surface.presentation.lock();
5037                *presentation = Some(present::Presentation {
5038                    device: Arc::clone(self),
5039                    config: config.clone(),
5040                    acquired_texture: None,
5041                });
5042            }
5043
5044            user_callbacks.fire();
5045            return None;
5046        };
5047
5048        Some(error)
5049    }
5050
5051    fn lose(&self, message: &str) {
5052        // Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device.
5053
5054        // Mark the device explicitly as invalid. This is checked in various
5055        // places to prevent new work from being submitted.
5056        self.valid.store(false, Ordering::Release);
5057
5058        // 1) Resolve the GPUDevice device.lost promise.
5059        if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
5060            device_lost_closure(DeviceLostReason::Unknown, message.to_string());
5061        }
5062
5063        // 2) Complete any outstanding mapAsync() steps.
5064        // 3) Complete any outstanding onSubmittedWorkDone() steps.
5065
5066        // These parts are passively accomplished by setting valid to false,
5067        // since that will prevent any new work from being added to the queues.
5068        // Future calls to poll_devices will continue to check the work queues
5069        // until they are cleared, and then drop the device.
5070    }
5071
5072    fn release_gpu_resources(&self) {
5073        // This is called when the device is lost, which makes every associated
5074        // resource invalid and unusable. This is an opportunity to release all of
5075        // the underlying gpu resources, even though the objects remain visible to
5076        // the user agent. We purge this memory naturally when resources have been
5077        // moved into the appropriate buckets, so this function just needs to
5078        // initiate movement into those buckets, and it can do that by calling
5079        // "destroy" on all the resources we know about.
5080
5081        // During these iterations, we discard all errors. We don't care!
5082        let trackers = self.trackers.lock();
5083        for buffer in trackers.buffers.used_resources() {
5084            if let Some(buffer) = Weak::upgrade(buffer) {
5085                buffer.destroy();
5086            }
5087        }
5088        for texture in trackers.textures.used_resources() {
5089            if let Some(texture) = Weak::upgrade(texture) {
5090                texture.destroy();
5091            }
5092        }
5093    }
5094
5095    pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {
5096        UsageScope::new_pooled(&self.usage_scopes, &self.tracker_indices)
5097    }
5098
5099    pub fn get_hal_counters(&self) -> wgt::HalCounters {
5100        self.raw().get_internal_counters()
5101    }
5102
5103    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
5104        self.raw().generate_allocator_report()
5105    }
5106}
5107
5108crate::impl_resource_type!(Device);
5109crate::impl_labeled!(Device);
5110crate::impl_storage_item!(Device);