wgpu_core/device/
resource.rs

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