wgpu_core/device/
resource.rs

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