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