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