Skip to main content

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