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