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