wgpu_core/device/
resource.rs

1#[cfg(feature = "trace")]
2use crate::device::trace;
3use crate::{
4    binding_model::{self, BindGroup, BindGroupLayout, BindGroupLayoutEntryError},
5    command, conv,
6    device::{
7        bgl, create_validator,
8        life::{LifetimeTracker, WaitIdleError},
9        map_buffer,
10        queue::PendingWrites,
11        AttachmentData, DeviceLostInvocation, HostMap, MissingDownlevelFlags, MissingFeatures,
12        RenderPassContext, CLEANUP_WAIT_MS,
13    },
14    hal_label,
15    init_tracker::{
16        BufferInitTracker, BufferInitTrackerAction, MemoryInitKind, TextureInitRange,
17        TextureInitTrackerAction,
18    },
19    instance::Adapter,
20    lock::{rank, Mutex, MutexGuard, RwLock},
21    pipeline,
22    pool::ResourcePool,
23    resource::{
24        self, Buffer, Fallible, Labeled, ParentDevice, QuerySet, Sampler, StagingBuffer, Texture,
25        TextureView, TextureViewNotRenderableReason, TrackingData,
26    },
27    resource_log,
28    snatch::{SnatchGuard, SnatchLock, Snatchable},
29    track::{
30        BindGroupStates, DeviceTracker, TextureSelector, TrackerIndexAllocators, UsageScope,
31        UsageScopePool,
32    },
33    validation::{self, validate_color_attachment_bytes_per_sample},
34    weak_vec::WeakVec,
35    FastHashMap, LabelHelpers, PreHashedKey, PreHashedMap,
36};
37
38use arrayvec::ArrayVec;
39use smallvec::SmallVec;
40use wgt::{
41    math::align_to, DeviceLostReason, TextureFormat, TextureSampleType, TextureViewDimension,
42};
43
44use std::{
45    borrow::Cow,
46    mem::{self, ManuallyDrop},
47    num::NonZeroU32,
48    sync::{
49        atomic::{AtomicBool, AtomicU64, Ordering},
50        Arc, OnceLock, Weak,
51    },
52};
53
54use super::{
55    queue::Queue, DeviceDescriptor, DeviceError, UserClosures, ENTRYPOINT_FAILURE_ERROR,
56    ZERO_BUFFER_SIZE,
57};
58
59/// Structure describing a logical device. Some members are internally mutable,
60/// stored behind mutexes.
61///
62/// TODO: establish clear order of locking for these:
63/// `life_tracker`, `trackers`, `render_passes`, `pending_writes`, `trace`.
64///
65/// Currently, the rules are:
66/// 1. `life_tracker` is locked after `hub.devices`, enforced by the type system
67/// 1. `self.trackers` is locked last (unenforced)
68/// 1. `self.trace` is locked last (unenforced)
69///
70/// Right now avoid locking twice same resource or registry in a call execution
71/// and minimize the locking to the minimum scope possible
72/// Unless otherwise specified, no lock may be acquired while holding another lock.
73/// This means that you must inspect function calls made while a lock is held
74/// to see what locks the callee may try to acquire.
75///
76/// Important:
77/// When locking pending_writes please check that trackers is not locked
78/// trackers should be locked only when needed for the shortest time possible
79pub struct Device {
80    raw: ManuallyDrop<Box<dyn hal::DynDevice>>,
81    pub(crate) adapter: Arc<Adapter>,
82    pub(crate) queue: OnceLock<Weak<Queue>>,
83    queue_to_drop: OnceLock<Box<dyn hal::DynQueue>>,
84    pub(crate) zero_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
85    /// The `label` from the descriptor used to create the resource.
86    label: String,
87
88    pub(crate) command_allocator: command::CommandAllocator,
89
90    /// The index of the last command submission that was attempted.
91    ///
92    /// Note that `fence` may never be signalled with this value, if the command
93    /// submission failed. If you need to wait for everything running on a
94    /// `Queue` to complete, wait for [`last_successful_submission_index`].
95    ///
96    /// [`last_successful_submission_index`]: Device::last_successful_submission_index
97    pub(crate) active_submission_index: hal::AtomicFenceValue,
98
99    /// The index of the last successful submission to this device's
100    /// [`hal::Queue`].
101    ///
102    /// Unlike [`active_submission_index`], which is incremented each time
103    /// submission is attempted, this is updated only when submission succeeds,
104    /// so waiting for this value won't hang waiting for work that was never
105    /// submitted.
106    ///
107    /// [`active_submission_index`]: Device::active_submission_index
108    pub(crate) last_successful_submission_index: hal::AtomicFenceValue,
109
110    // NOTE: if both are needed, the `snatchable_lock` must be consistently acquired before the
111    // `fence` lock to avoid deadlocks.
112    pub(crate) fence: RwLock<ManuallyDrop<Box<dyn hal::DynFence>>>,
113    pub(crate) snatchable_lock: SnatchLock,
114
115    /// Is this device valid? Valid is closely associated with "lose the device",
116    /// which can be triggered by various methods, including at the end of device
117    /// destroy, and by any GPU errors that cause us to no longer trust the state
118    /// of the device. Ideally we would like to fold valid into the storage of
119    /// the device itself (for example as an Error enum), but unfortunately we
120    /// need to continue to be able to retrieve the device in poll_devices to
121    /// determine if it can be dropped. If our internal accesses of devices were
122    /// done through ref-counted references and external accesses checked for
123    /// Error enums, we wouldn't need this. For now, we need it. All the call
124    /// sites where we check it are areas that should be revisited if we start
125    /// using ref-counted references for internal access.
126    pub(crate) valid: AtomicBool,
127
128    /// All live resources allocated with this [`Device`].
129    ///
130    /// Has to be locked temporarily only (locked last)
131    /// and never before pending_writes
132    pub(crate) trackers: Mutex<DeviceTracker>,
133    pub(crate) tracker_indices: TrackerIndexAllocators,
134    // Life tracker should be locked right after the device and before anything else.
135    life_tracker: Mutex<LifetimeTracker>,
136    /// Pool of bind group layouts, allowing deduplication.
137    pub(crate) bgl_pool: ResourcePool<bgl::EntryMap, BindGroupLayout>,
138    pub(crate) alignments: hal::Alignments,
139    pub(crate) limits: wgt::Limits,
140    pub(crate) features: wgt::Features,
141    pub(crate) downlevel: wgt::DownlevelCapabilities,
142    pub(crate) instance_flags: wgt::InstanceFlags,
143    pub(crate) pending_writes: Mutex<ManuallyDrop<PendingWrites>>,
144    pub(crate) deferred_destroy: Mutex<Vec<DeferredDestroy>>,
145    #[cfg(feature = "trace")]
146    pub(crate) trace: Mutex<Option<trace::Trace>>,
147    pub(crate) usage_scopes: UsageScopePool,
148
149    #[cfg(feature = "indirect-validation")]
150    pub(crate) indirect_validation: Option<crate::indirect_validation::IndirectValidation>,
151}
152
153pub(crate) enum DeferredDestroy {
154    TextureViews(WeakVec<TextureView>),
155    BindGroups(WeakVec<BindGroup>),
156}
157
158impl std::fmt::Debug for Device {
159    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160        f.debug_struct("Device")
161            .field("label", &self.label())
162            .field("limits", &self.limits)
163            .field("features", &self.features)
164            .field("downlevel", &self.downlevel)
165            .finish()
166    }
167}
168
169impl Drop for Device {
170    fn drop(&mut self) {
171        resource_log!("Drop {}", self.error_ident());
172        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
173        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
174        // SAFETY: We are in the Drop impl and we don't use self.zero_buffer anymore after this point.
175        let zero_buffer = unsafe { ManuallyDrop::take(&mut self.zero_buffer) };
176        // SAFETY: We are in the Drop impl and we don't use self.pending_writes anymore after this point.
177        let pending_writes = unsafe { ManuallyDrop::take(&mut self.pending_writes.lock()) };
178        // SAFETY: We are in the Drop impl and we don't use self.fence anymore after this point.
179        let fence = unsafe { ManuallyDrop::take(&mut self.fence.write()) };
180        pending_writes.dispose(raw.as_ref());
181        self.command_allocator.dispose(raw.as_ref());
182        #[cfg(feature = "indirect-validation")]
183        self.indirect_validation
184            .take()
185            .unwrap()
186            .dispose(raw.as_ref());
187        unsafe {
188            raw.destroy_buffer(zero_buffer);
189            raw.destroy_fence(fence);
190            let queue = self.queue_to_drop.take().unwrap();
191            raw.exit(queue);
192        }
193    }
194}
195
196impl Device {
197    pub(crate) fn raw(&self) -> &dyn hal::DynDevice {
198        self.raw.as_ref()
199    }
200    pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {
201        if self.features.contains(feature) {
202            Ok(())
203        } else {
204            Err(MissingFeatures(feature))
205        }
206    }
207
208    pub(crate) fn require_downlevel_flags(
209        &self,
210        flags: wgt::DownlevelFlags,
211    ) -> Result<(), MissingDownlevelFlags> {
212        if self.downlevel.flags.contains(flags) {
213            Ok(())
214        } else {
215            Err(MissingDownlevelFlags(flags))
216        }
217    }
218}
219
220impl Device {
221    pub(crate) fn new(
222        raw_device: Box<dyn hal::DynDevice>,
223        raw_queue: &dyn hal::DynQueue,
224        adapter: &Arc<Adapter>,
225        desc: &DeviceDescriptor,
226        trace_path: Option<&std::path::Path>,
227        instance_flags: wgt::InstanceFlags,
228    ) -> Result<Self, DeviceError> {
229        #[cfg(not(feature = "trace"))]
230        if let Some(_) = trace_path {
231            log::error!("Feature 'trace' is not enabled");
232        }
233        let fence = unsafe { raw_device.create_fence() }.map_err(DeviceError::from_hal)?;
234
235        let command_allocator = command::CommandAllocator::new();
236        let pending_encoder = command_allocator
237            .acquire_encoder(raw_device.as_ref(), raw_queue)
238            .map_err(DeviceError::from_hal)?;
239        let mut pending_writes = PendingWrites::new(pending_encoder);
240
241        // Create zeroed buffer used for texture clears.
242        let zero_buffer = unsafe {
243            raw_device.create_buffer(&hal::BufferDescriptor {
244                label: hal_label(Some("(wgpu internal) zero init buffer"), instance_flags),
245                size: ZERO_BUFFER_SIZE,
246                usage: hal::BufferUses::COPY_SRC | hal::BufferUses::COPY_DST,
247                memory_flags: hal::MemoryFlags::empty(),
248            })
249        }
250        .map_err(DeviceError::from_hal)?;
251        pending_writes.activate();
252        unsafe {
253            pending_writes
254                .command_encoder
255                .transition_buffers(&[hal::BufferBarrier {
256                    buffer: zero_buffer.as_ref(),
257                    usage: hal::BufferUses::empty()..hal::BufferUses::COPY_DST,
258                }]);
259            pending_writes
260                .command_encoder
261                .clear_buffer(zero_buffer.as_ref(), 0..ZERO_BUFFER_SIZE);
262            pending_writes
263                .command_encoder
264                .transition_buffers(&[hal::BufferBarrier {
265                    buffer: zero_buffer.as_ref(),
266                    usage: hal::BufferUses::COPY_DST..hal::BufferUses::COPY_SRC,
267                }]);
268        }
269
270        let alignments = adapter.raw.capabilities.alignments.clone();
271        let downlevel = adapter.raw.capabilities.downlevel.clone();
272
273        #[cfg(feature = "indirect-validation")]
274        let indirect_validation = if downlevel
275            .flags
276            .contains(wgt::DownlevelFlags::INDIRECT_EXECUTION)
277        {
278            match crate::indirect_validation::IndirectValidation::new(
279                raw_device.as_ref(),
280                &desc.required_limits,
281            ) {
282                Ok(indirect_validation) => Some(indirect_validation),
283                Err(e) => {
284                    log::error!("indirect-validation error: {e:?}");
285                    return Err(DeviceError::Lost);
286                }
287            }
288        } else {
289            None
290        };
291
292        Ok(Self {
293            raw: ManuallyDrop::new(raw_device),
294            adapter: adapter.clone(),
295            queue: OnceLock::new(),
296            queue_to_drop: OnceLock::new(),
297            zero_buffer: ManuallyDrop::new(zero_buffer),
298            label: desc.label.to_string(),
299            command_allocator,
300            active_submission_index: AtomicU64::new(0),
301            last_successful_submission_index: AtomicU64::new(0),
302            fence: RwLock::new(rank::DEVICE_FENCE, ManuallyDrop::new(fence)),
303            snatchable_lock: unsafe { SnatchLock::new(rank::DEVICE_SNATCHABLE_LOCK) },
304            valid: AtomicBool::new(true),
305            trackers: Mutex::new(rank::DEVICE_TRACKERS, DeviceTracker::new()),
306            tracker_indices: TrackerIndexAllocators::new(),
307            life_tracker: Mutex::new(rank::DEVICE_LIFE_TRACKER, LifetimeTracker::new()),
308            bgl_pool: ResourcePool::new(),
309            #[cfg(feature = "trace")]
310            trace: Mutex::new(
311                rank::DEVICE_TRACE,
312                trace_path.and_then(|path| match trace::Trace::new(path) {
313                    Ok(mut trace) => {
314                        trace.add(trace::Action::Init {
315                            desc: desc.clone(),
316                            backend: adapter.backend(),
317                        });
318                        Some(trace)
319                    }
320                    Err(e) => {
321                        log::error!("Unable to start a trace in '{path:?}': {e}");
322                        None
323                    }
324                }),
325            ),
326            alignments,
327            limits: desc.required_limits.clone(),
328            features: desc.required_features,
329            downlevel,
330            instance_flags,
331            pending_writes: Mutex::new(
332                rank::DEVICE_PENDING_WRITES,
333                ManuallyDrop::new(pending_writes),
334            ),
335            deferred_destroy: Mutex::new(rank::DEVICE_DEFERRED_DESTROY, Vec::new()),
336            usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()),
337            #[cfg(feature = "indirect-validation")]
338            indirect_validation,
339        })
340    }
341
342    /// Returns the backend this device is using.
343    pub fn backend(&self) -> wgt::Backend {
344        self.adapter.backend()
345    }
346
347    pub fn is_valid(&self) -> bool {
348        self.valid.load(Ordering::Acquire)
349    }
350
351    pub fn check_is_valid(&self) -> Result<(), DeviceError> {
352        if self.is_valid() {
353            Ok(())
354        } else {
355            Err(DeviceError::Invalid(self.error_ident()))
356        }
357    }
358
359    pub fn handle_hal_error(&self, error: hal::DeviceError) -> DeviceError {
360        match error {
361            hal::DeviceError::OutOfMemory => {}
362            hal::DeviceError::Lost
363            | hal::DeviceError::ResourceCreationFailed
364            | hal::DeviceError::Unexpected => {
365                self.lose(&error.to_string());
366            }
367        }
368        DeviceError::from_hal(error)
369    }
370
371    pub(crate) fn release_queue(&self, queue: Box<dyn hal::DynQueue>) {
372        assert!(self.queue_to_drop.set(queue).is_ok());
373    }
374
375    #[track_caller]
376    pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker> {
377        self.life_tracker.lock()
378    }
379
380    /// Run some destroy operations that were deferred.
381    ///
382    /// Destroying the resources requires taking a write lock on the device's snatch lock,
383    /// so a good reason for deferring resource destruction is when we don't know for sure
384    /// how risky it is to take the lock (typically, it shouldn't be taken from the drop
385    /// implementation of a reference-counted structure).
386    /// The snatch lock must not be held while this function is called.
387    pub(crate) fn deferred_resource_destruction(&self) {
388        let deferred_destroy = mem::take(&mut *self.deferred_destroy.lock());
389        for item in deferred_destroy {
390            match item {
391                DeferredDestroy::TextureViews(views) => {
392                    for view in views {
393                        let Some(view) = view.upgrade() else {
394                            continue;
395                        };
396                        let Some(raw_view) = view.raw.snatch(&mut self.snatchable_lock.write())
397                        else {
398                            continue;
399                        };
400
401                        resource_log!("Destroy raw {}", view.error_ident());
402
403                        unsafe {
404                            self.raw().destroy_texture_view(raw_view);
405                        }
406                    }
407                }
408                DeferredDestroy::BindGroups(bind_groups) => {
409                    for bind_group in bind_groups {
410                        let Some(bind_group) = bind_group.upgrade() else {
411                            continue;
412                        };
413                        let Some(raw_bind_group) =
414                            bind_group.raw.snatch(&mut self.snatchable_lock.write())
415                        else {
416                            continue;
417                        };
418
419                        resource_log!("Destroy raw {}", bind_group.error_ident());
420
421                        unsafe {
422                            self.raw().destroy_bind_group(raw_bind_group);
423                        }
424                    }
425                }
426            }
427        }
428    }
429
430    pub fn get_queue(&self) -> Option<Arc<Queue>> {
431        self.queue.get().as_ref()?.upgrade()
432    }
433
434    pub fn set_queue(&self, queue: &Arc<Queue>) {
435        assert!(self.queue.set(Arc::downgrade(queue)).is_ok());
436    }
437
438    /// Check this device for completed commands.
439    ///
440    /// The `maintain` argument tells how the maintenance function should behave, either
441    /// blocking or just polling the current state of the gpu.
442    ///
443    /// Return a pair `(closures, queue_empty)`, where:
444    ///
445    /// - `closures` is a list of actions to take: mapping buffers, notifying the user
446    ///
447    /// - `queue_empty` is a boolean indicating whether there are more queue
448    ///   submissions still in flight. (We have to take the locks needed to
449    ///   produce this information for other reasons, so we might as well just
450    ///   return it to our callers.)
451    pub(crate) fn maintain<'this>(
452        &'this self,
453        fence: crate::lock::RwLockReadGuard<ManuallyDrop<Box<dyn hal::DynFence>>>,
454        maintain: wgt::Maintain<crate::SubmissionIndex>,
455        snatch_guard: SnatchGuard,
456    ) -> Result<(UserClosures, bool), WaitIdleError> {
457        profiling::scope!("Device::maintain");
458
459        // Determine which submission index `maintain` represents.
460        let submission_index = match maintain {
461            wgt::Maintain::WaitForSubmissionIndex(submission_index) => {
462                let last_successful_submission_index = self
463                    .last_successful_submission_index
464                    .load(Ordering::Acquire);
465
466                if submission_index > last_successful_submission_index {
467                    return Err(WaitIdleError::WrongSubmissionIndex(
468                        submission_index,
469                        last_successful_submission_index,
470                    ));
471                }
472
473                submission_index
474            }
475            wgt::Maintain::Wait => self
476                .last_successful_submission_index
477                .load(Ordering::Acquire),
478            wgt::Maintain::Poll => unsafe { self.raw().get_fence_value(fence.as_ref()) }
479                .map_err(|e| self.handle_hal_error(e))?,
480        };
481
482        // If necessary, wait for that submission to complete.
483        if maintain.is_wait() {
484            log::trace!("Device::maintain: waiting for submission index {submission_index}");
485            unsafe {
486                self.raw()
487                    .wait(fence.as_ref(), submission_index, CLEANUP_WAIT_MS)
488            }
489            .map_err(|e| self.handle_hal_error(e))?;
490        }
491
492        let mut life_tracker = self.lock_life();
493        let submission_closures =
494            life_tracker.triage_submissions(submission_index, &self.command_allocator);
495
496        life_tracker.triage_mapped();
497
498        let mapping_closures = life_tracker.handle_mapping(self.raw(), &snatch_guard);
499
500        let queue_empty = life_tracker.queue_empty();
501
502        // Detect if we have been destroyed and now need to lose the device.
503        // If we are invalid (set at start of destroy) and our queue is empty,
504        // and we have a DeviceLostClosure, return the closure to be called by
505        // our caller. This will complete the steps for both destroy and for
506        // "lose the device".
507        let mut device_lost_invocations = SmallVec::new();
508        let mut should_release_gpu_resource = false;
509        if !self.is_valid() && queue_empty {
510            // We can release gpu resources associated with this device (but not
511            // while holding the life_tracker lock).
512            should_release_gpu_resource = true;
513
514            // If we have a DeviceLostClosure, build an invocation with the
515            // reason DeviceLostReason::Destroyed and no message.
516            if life_tracker.device_lost_closure.is_some() {
517                device_lost_invocations.push(DeviceLostInvocation {
518                    closure: life_tracker.device_lost_closure.take().unwrap(),
519                    reason: DeviceLostReason::Destroyed,
520                    message: String::new(),
521                });
522            }
523        }
524
525        // Don't hold the locks while calling release_gpu_resources.
526        drop(life_tracker);
527        drop(fence);
528        drop(snatch_guard);
529
530        if should_release_gpu_resource {
531            self.release_gpu_resources();
532        }
533
534        let closures = UserClosures {
535            mappings: mapping_closures,
536            submissions: submission_closures,
537            device_lost_invocations,
538        };
539        Ok((closures, queue_empty))
540    }
541
542    pub(crate) fn create_buffer(
543        self: &Arc<Self>,
544        desc: &resource::BufferDescriptor,
545    ) -> Result<Arc<Buffer>, resource::CreateBufferError> {
546        self.check_is_valid()?;
547
548        if desc.size > self.limits.max_buffer_size {
549            return Err(resource::CreateBufferError::MaxBufferSize {
550                requested: desc.size,
551                maximum: self.limits.max_buffer_size,
552            });
553        }
554
555        if desc.usage.contains(wgt::BufferUsages::INDEX)
556            && desc.usage.contains(
557                wgt::BufferUsages::VERTEX
558                    | wgt::BufferUsages::UNIFORM
559                    | wgt::BufferUsages::INDIRECT
560                    | wgt::BufferUsages::STORAGE,
561            )
562        {
563            self.require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)?;
564        }
565
566        if desc.usage.is_empty() || desc.usage.contains_invalid_bits() {
567            return Err(resource::CreateBufferError::InvalidUsage(desc.usage));
568        }
569
570        if !self
571            .features
572            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
573        {
574            use wgt::BufferUsages as Bu;
575            let write_mismatch = desc.usage.contains(Bu::MAP_WRITE)
576                && !(Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage);
577            let read_mismatch = desc.usage.contains(Bu::MAP_READ)
578                && !(Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage);
579            if write_mismatch || read_mismatch {
580                return Err(resource::CreateBufferError::UsageMismatch(desc.usage));
581            }
582        }
583
584        let mut usage = conv::map_buffer_usage(desc.usage);
585
586        if desc.usage.contains(wgt::BufferUsages::INDIRECT) {
587            self.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
588            // We are going to be reading from it, internally;
589            // when validating the content of the buffer
590            usage |= hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_READ_WRITE;
591        }
592
593        if desc.mapped_at_creation {
594            if desc.size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
595                return Err(resource::CreateBufferError::UnalignedSize);
596            }
597            if !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
598                // we are going to be copying into it, internally
599                usage |= hal::BufferUses::COPY_DST;
600            }
601        } else {
602            // We are required to zero out (initialize) all memory. This is done
603            // on demand using clear_buffer which requires write transfer usage!
604            usage |= hal::BufferUses::COPY_DST;
605        }
606
607        let actual_size = if desc.size == 0 {
608            wgt::COPY_BUFFER_ALIGNMENT
609        } else if desc.usage.contains(wgt::BufferUsages::VERTEX) {
610            // Bumping the size by 1 so that we can bind an empty range at the
611            // end of the buffer.
612            desc.size + 1
613        } else {
614            desc.size
615        };
616        let clear_remainder = actual_size % wgt::COPY_BUFFER_ALIGNMENT;
617        let aligned_size = if clear_remainder != 0 {
618            actual_size + wgt::COPY_BUFFER_ALIGNMENT - clear_remainder
619        } else {
620            actual_size
621        };
622
623        let hal_desc = hal::BufferDescriptor {
624            label: desc.label.to_hal(self.instance_flags),
625            size: aligned_size,
626            usage,
627            memory_flags: hal::MemoryFlags::empty(),
628        };
629        let buffer =
630            unsafe { self.raw().create_buffer(&hal_desc) }.map_err(|e| self.handle_hal_error(e))?;
631
632        #[cfg(feature = "indirect-validation")]
633        let raw_indirect_validation_bind_group =
634            self.create_indirect_validation_bind_group(buffer.as_ref(), desc.size, desc.usage)?;
635
636        let buffer = Buffer {
637            raw: Snatchable::new(buffer),
638            device: self.clone(),
639            usage: desc.usage,
640            size: desc.size,
641            initialization_status: RwLock::new(
642                rank::BUFFER_INITIALIZATION_STATUS,
643                BufferInitTracker::new(aligned_size),
644            ),
645            map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
646            label: desc.label.to_string(),
647            tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),
648            bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
649            #[cfg(feature = "indirect-validation")]
650            raw_indirect_validation_bind_group,
651        };
652
653        let buffer = Arc::new(buffer);
654
655        let buffer_use = if !desc.mapped_at_creation {
656            hal::BufferUses::empty()
657        } else if desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
658            // buffer is mappable, so we are just doing that at start
659            let map_size = buffer.size;
660            let mapping = if map_size == 0 {
661                hal::BufferMapping {
662                    ptr: std::ptr::NonNull::dangling(),
663                    is_coherent: true,
664                }
665            } else {
666                let snatch_guard: SnatchGuard = self.snatchable_lock.read();
667                map_buffer(
668                    self.raw(),
669                    &buffer,
670                    0,
671                    map_size,
672                    HostMap::Write,
673                    &snatch_guard,
674                )?
675            };
676            *buffer.map_state.lock() = resource::BufferMapState::Active {
677                mapping,
678                range: 0..map_size,
679                host: HostMap::Write,
680            };
681            hal::BufferUses::MAP_WRITE
682        } else {
683            let mut staging_buffer =
684                StagingBuffer::new(self, wgt::BufferSize::new(aligned_size).unwrap())?;
685
686            // Zero initialize memory and then mark the buffer as initialized
687            // (it's guaranteed that this is the case by the time the buffer is usable)
688            staging_buffer.write_zeros();
689            buffer.initialization_status.write().drain(0..aligned_size);
690
691            *buffer.map_state.lock() = resource::BufferMapState::Init { staging_buffer };
692            hal::BufferUses::COPY_DST
693        };
694
695        self.trackers
696            .lock()
697            .buffers
698            .insert_single(&buffer, buffer_use);
699
700        Ok(buffer)
701    }
702
703    pub(crate) fn create_texture_from_hal(
704        self: &Arc<Self>,
705        hal_texture: Box<dyn hal::DynTexture>,
706        desc: &resource::TextureDescriptor,
707    ) -> Result<Arc<Texture>, resource::CreateTextureError> {
708        let format_features = self
709            .describe_format_features(desc.format)
710            .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?;
711
712        unsafe { self.raw().add_raw_texture(&*hal_texture) };
713
714        let texture = Texture::new(
715            self,
716            resource::TextureInner::Native { raw: hal_texture },
717            conv::map_texture_usage(desc.usage, desc.format.into()),
718            desc,
719            format_features,
720            resource::TextureClearMode::None,
721            false,
722        );
723
724        let texture = Arc::new(texture);
725
726        self.trackers
727            .lock()
728            .textures
729            .insert_single(&texture, hal::TextureUses::UNINITIALIZED);
730
731        Ok(texture)
732    }
733
734    pub(crate) fn create_buffer_from_hal(
735        self: &Arc<Self>,
736        hal_buffer: Box<dyn hal::DynBuffer>,
737        desc: &resource::BufferDescriptor,
738    ) -> (Fallible<Buffer>, Option<resource::CreateBufferError>) {
739        #[cfg(feature = "indirect-validation")]
740        let raw_indirect_validation_bind_group = match self.create_indirect_validation_bind_group(
741            hal_buffer.as_ref(),
742            desc.size,
743            desc.usage,
744        ) {
745            Ok(ok) => ok,
746            Err(e) => return (Fallible::Invalid(Arc::new(desc.label.to_string())), Some(e)),
747        };
748
749        unsafe { self.raw().add_raw_buffer(&*hal_buffer) };
750
751        let buffer = Buffer {
752            raw: Snatchable::new(hal_buffer),
753            device: self.clone(),
754            usage: desc.usage,
755            size: desc.size,
756            initialization_status: RwLock::new(
757                rank::BUFFER_INITIALIZATION_STATUS,
758                BufferInitTracker::new(0),
759            ),
760            map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
761            label: desc.label.to_string(),
762            tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),
763            bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
764            #[cfg(feature = "indirect-validation")]
765            raw_indirect_validation_bind_group,
766        };
767
768        let buffer = Arc::new(buffer);
769
770        self.trackers
771            .lock()
772            .buffers
773            .insert_single(&buffer, hal::BufferUses::empty());
774
775        (Fallible::Valid(buffer), None)
776    }
777
778    #[cfg(feature = "indirect-validation")]
779    fn create_indirect_validation_bind_group(
780        &self,
781        raw_buffer: &dyn hal::DynBuffer,
782        buffer_size: u64,
783        usage: wgt::BufferUsages,
784    ) -> Result<Snatchable<Box<dyn hal::DynBindGroup>>, resource::CreateBufferError> {
785        if usage.contains(wgt::BufferUsages::INDIRECT) {
786            let indirect_validation = self.indirect_validation.as_ref().unwrap();
787            let bind_group = indirect_validation
788                .create_src_bind_group(self.raw(), &self.limits, buffer_size, raw_buffer)
789                .map_err(resource::CreateBufferError::IndirectValidationBindGroup)?;
790            match bind_group {
791                Some(bind_group) => Ok(Snatchable::new(bind_group)),
792                None => Ok(Snatchable::empty()),
793            }
794        } else {
795            Ok(Snatchable::empty())
796        }
797    }
798
799    pub(crate) fn create_texture(
800        self: &Arc<Self>,
801        desc: &resource::TextureDescriptor,
802    ) -> Result<Arc<Texture>, resource::CreateTextureError> {
803        use resource::{CreateTextureError, TextureDimensionError};
804
805        self.check_is_valid()?;
806
807        if desc.usage.is_empty() || desc.usage.contains_invalid_bits() {
808            return Err(CreateTextureError::InvalidUsage(desc.usage));
809        }
810
811        conv::check_texture_dimension_size(
812            desc.dimension,
813            desc.size,
814            desc.sample_count,
815            &self.limits,
816        )?;
817
818        if desc.dimension != wgt::TextureDimension::D2 {
819            // Depth textures can only be 2D
820            if desc.format.is_depth_stencil_format() {
821                return Err(CreateTextureError::InvalidDepthDimension(
822                    desc.dimension,
823                    desc.format,
824                ));
825            }
826            // Renderable textures can only be 2D
827            if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
828                return Err(CreateTextureError::InvalidDimensionUsages(
829                    wgt::TextureUsages::RENDER_ATTACHMENT,
830                    desc.dimension,
831                ));
832            }
833        }
834
835        if desc.dimension != wgt::TextureDimension::D2
836            && desc.dimension != wgt::TextureDimension::D3
837        {
838            // Compressed textures can only be 2D or 3D
839            if desc.format.is_compressed() {
840                return Err(CreateTextureError::InvalidCompressedDimension(
841                    desc.dimension,
842                    desc.format,
843                ));
844            }
845        }
846
847        if desc.format.is_compressed() {
848            let (block_width, block_height) = desc.format.block_dimensions();
849
850            if desc.size.width % block_width != 0 {
851                return Err(CreateTextureError::InvalidDimension(
852                    TextureDimensionError::NotMultipleOfBlockWidth {
853                        width: desc.size.width,
854                        block_width,
855                        format: desc.format,
856                    },
857                ));
858            }
859
860            if desc.size.height % block_height != 0 {
861                return Err(CreateTextureError::InvalidDimension(
862                    TextureDimensionError::NotMultipleOfBlockHeight {
863                        height: desc.size.height,
864                        block_height,
865                        format: desc.format,
866                    },
867                ));
868            }
869
870            if desc.dimension == wgt::TextureDimension::D3 {
871                // Only BCn formats with Sliced 3D feature can be used for 3D textures
872                if desc.format.is_bcn() {
873                    self.require_features(wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D)
874                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
875                } else {
876                    return Err(CreateTextureError::InvalidCompressedDimension(
877                        desc.dimension,
878                        desc.format,
879                    ));
880                }
881            }
882        }
883
884        {
885            let (width_multiple, height_multiple) = desc.format.size_multiple_requirement();
886
887            if desc.size.width % width_multiple != 0 {
888                return Err(CreateTextureError::InvalidDimension(
889                    TextureDimensionError::WidthNotMultipleOf {
890                        width: desc.size.width,
891                        multiple: width_multiple,
892                        format: desc.format,
893                    },
894                ));
895            }
896
897            if desc.size.height % height_multiple != 0 {
898                return Err(CreateTextureError::InvalidDimension(
899                    TextureDimensionError::HeightNotMultipleOf {
900                        height: desc.size.height,
901                        multiple: height_multiple,
902                        format: desc.format,
903                    },
904                ));
905            }
906        }
907
908        let format_features = self
909            .describe_format_features(desc.format)
910            .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
911
912        if desc.sample_count > 1 {
913            if desc.mip_level_count != 1 {
914                return Err(CreateTextureError::InvalidMipLevelCount {
915                    requested: desc.mip_level_count,
916                    maximum: 1,
917                });
918            }
919
920            if desc.size.depth_or_array_layers != 1 {
921                return Err(CreateTextureError::InvalidDimension(
922                    TextureDimensionError::MultisampledDepthOrArrayLayer(
923                        desc.size.depth_or_array_layers,
924                    ),
925                ));
926            }
927
928            if desc.usage.contains(wgt::TextureUsages::STORAGE_BINDING) {
929                return Err(CreateTextureError::InvalidMultisampledStorageBinding);
930            }
931
932            if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
933                return Err(CreateTextureError::MultisampledNotRenderAttachment);
934            }
935
936            if !format_features.flags.intersects(
937                wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4
938                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2
939                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8
940                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
941            ) {
942                return Err(CreateTextureError::InvalidMultisampledFormat(desc.format));
943            }
944
945            if !format_features
946                .flags
947                .sample_count_supported(desc.sample_count)
948            {
949                return Err(CreateTextureError::InvalidSampleCount(
950                    desc.sample_count,
951                    desc.format,
952                    desc.format
953                        .guaranteed_format_features(self.features)
954                        .flags
955                        .supported_sample_counts(),
956                    self.adapter
957                        .get_texture_format_features(desc.format)
958                        .flags
959                        .supported_sample_counts(),
960                ));
961            };
962        }
963
964        let mips = desc.mip_level_count;
965        let max_levels_allowed = desc.size.max_mips(desc.dimension).min(hal::MAX_MIP_LEVELS);
966        if mips == 0 || mips > max_levels_allowed {
967            return Err(CreateTextureError::InvalidMipLevelCount {
968                requested: mips,
969                maximum: max_levels_allowed,
970            });
971        }
972
973        let missing_allowed_usages = desc.usage - format_features.allowed_usages;
974        if !missing_allowed_usages.is_empty() {
975            // detect downlevel incompatibilities
976            let wgpu_allowed_usages = desc
977                .format
978                .guaranteed_format_features(self.features)
979                .allowed_usages;
980            let wgpu_missing_usages = desc.usage - wgpu_allowed_usages;
981            return Err(CreateTextureError::InvalidFormatUsages(
982                missing_allowed_usages,
983                desc.format,
984                wgpu_missing_usages.is_empty(),
985            ));
986        }
987
988        let mut hal_view_formats = vec![];
989        for format in desc.view_formats.iter() {
990            if desc.format == *format {
991                continue;
992            }
993            if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
994                return Err(CreateTextureError::InvalidViewFormat(*format, desc.format));
995            }
996            hal_view_formats.push(*format);
997        }
998        if !hal_view_formats.is_empty() {
999            self.require_downlevel_flags(wgt::DownlevelFlags::VIEW_FORMATS)?;
1000        }
1001
1002        let hal_usage = conv::map_texture_usage_for_texture(desc, &format_features);
1003
1004        let hal_desc = hal::TextureDescriptor {
1005            label: desc.label.to_hal(self.instance_flags),
1006            size: desc.size,
1007            mip_level_count: desc.mip_level_count,
1008            sample_count: desc.sample_count,
1009            dimension: desc.dimension,
1010            format: desc.format,
1011            usage: hal_usage,
1012            memory_flags: hal::MemoryFlags::empty(),
1013            view_formats: hal_view_formats,
1014        };
1015
1016        let raw_texture = unsafe { self.raw().create_texture(&hal_desc) }
1017            .map_err(|e| self.handle_hal_error(e))?;
1018
1019        let clear_mode = if hal_usage
1020            .intersects(hal::TextureUses::DEPTH_STENCIL_WRITE | hal::TextureUses::COLOR_TARGET)
1021        {
1022            let (is_color, usage) = if desc.format.is_depth_stencil_format() {
1023                (false, hal::TextureUses::DEPTH_STENCIL_WRITE)
1024            } else {
1025                (true, hal::TextureUses::COLOR_TARGET)
1026            };
1027            let dimension = match desc.dimension {
1028                wgt::TextureDimension::D1 => TextureViewDimension::D1,
1029                wgt::TextureDimension::D2 => TextureViewDimension::D2,
1030                wgt::TextureDimension::D3 => unreachable!(),
1031            };
1032
1033            let clear_label = hal_label(
1034                Some("(wgpu internal) clear texture view"),
1035                self.instance_flags,
1036            );
1037
1038            let mut clear_views = SmallVec::new();
1039            for mip_level in 0..desc.mip_level_count {
1040                for array_layer in 0..desc.size.depth_or_array_layers {
1041                    macro_rules! push_clear_view {
1042                        ($format:expr, $aspect:expr) => {
1043                            let desc = hal::TextureViewDescriptor {
1044                                label: clear_label,
1045                                format: $format,
1046                                dimension,
1047                                usage,
1048                                range: wgt::ImageSubresourceRange {
1049                                    aspect: $aspect,
1050                                    base_mip_level: mip_level,
1051                                    mip_level_count: Some(1),
1052                                    base_array_layer: array_layer,
1053                                    array_layer_count: Some(1),
1054                                },
1055                            };
1056                            clear_views.push(ManuallyDrop::new(
1057                                unsafe {
1058                                    self.raw().create_texture_view(raw_texture.as_ref(), &desc)
1059                                }
1060                                .map_err(|e| self.handle_hal_error(e))?,
1061                            ));
1062                        };
1063                    }
1064
1065                    if let Some(planes) = desc.format.planes() {
1066                        for plane in 0..planes {
1067                            let aspect = wgt::TextureAspect::from_plane(plane).unwrap();
1068                            let format = desc.format.aspect_specific_format(aspect).unwrap();
1069                            push_clear_view!(format, aspect);
1070                        }
1071                    } else {
1072                        push_clear_view!(desc.format, wgt::TextureAspect::All);
1073                    }
1074                }
1075            }
1076            resource::TextureClearMode::RenderPass {
1077                clear_views,
1078                is_color,
1079            }
1080        } else {
1081            resource::TextureClearMode::BufferCopy
1082        };
1083
1084        let texture = Texture::new(
1085            self,
1086            resource::TextureInner::Native { raw: raw_texture },
1087            hal_usage,
1088            desc,
1089            format_features,
1090            clear_mode,
1091            true,
1092        );
1093
1094        let texture = Arc::new(texture);
1095
1096        self.trackers
1097            .lock()
1098            .textures
1099            .insert_single(&texture, hal::TextureUses::UNINITIALIZED);
1100
1101        Ok(texture)
1102    }
1103
1104    pub(crate) fn create_texture_view(
1105        self: &Arc<Self>,
1106        texture: &Arc<Texture>,
1107        desc: &resource::TextureViewDescriptor,
1108    ) -> Result<Arc<TextureView>, resource::CreateTextureViewError> {
1109        self.check_is_valid()?;
1110
1111        let snatch_guard = texture.device.snatchable_lock.read();
1112
1113        let texture_raw = texture.try_raw(&snatch_guard)?;
1114
1115        // resolve TextureViewDescriptor defaults
1116        // https://gpuweb.github.io/gpuweb/#abstract-opdef-resolving-gputextureviewdescriptor-defaults
1117        let resolved_format = desc.format.unwrap_or_else(|| {
1118            texture
1119                .desc
1120                .format
1121                .aspect_specific_format(desc.range.aspect)
1122                .unwrap_or(texture.desc.format)
1123        });
1124
1125        let resolved_dimension = desc
1126            .dimension
1127            .unwrap_or_else(|| match texture.desc.dimension {
1128                wgt::TextureDimension::D1 => TextureViewDimension::D1,
1129                wgt::TextureDimension::D2 => {
1130                    if texture.desc.array_layer_count() == 1 {
1131                        TextureViewDimension::D2
1132                    } else {
1133                        TextureViewDimension::D2Array
1134                    }
1135                }
1136                wgt::TextureDimension::D3 => TextureViewDimension::D3,
1137            });
1138
1139        let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {
1140            texture
1141                .desc
1142                .mip_level_count
1143                .saturating_sub(desc.range.base_mip_level)
1144        });
1145
1146        let resolved_array_layer_count =
1147            desc.range
1148                .array_layer_count
1149                .unwrap_or_else(|| match resolved_dimension {
1150                    TextureViewDimension::D1
1151                    | TextureViewDimension::D2
1152                    | TextureViewDimension::D3 => 1,
1153                    TextureViewDimension::Cube => 6,
1154                    TextureViewDimension::D2Array | TextureViewDimension::CubeArray => texture
1155                        .desc
1156                        .array_layer_count()
1157                        .saturating_sub(desc.range.base_array_layer),
1158                });
1159
1160        // validate TextureViewDescriptor
1161
1162        let aspects = hal::FormatAspects::new(texture.desc.format, desc.range.aspect);
1163        if aspects.is_empty() {
1164            return Err(resource::CreateTextureViewError::InvalidAspect {
1165                texture_format: texture.desc.format,
1166                requested_aspect: desc.range.aspect,
1167            });
1168        }
1169
1170        let format_is_good = if desc.range.aspect == wgt::TextureAspect::All {
1171            resolved_format == texture.desc.format
1172                || texture.desc.view_formats.contains(&resolved_format)
1173        } else {
1174            Some(resolved_format)
1175                == texture
1176                    .desc
1177                    .format
1178                    .aspect_specific_format(desc.range.aspect)
1179        };
1180        if !format_is_good {
1181            return Err(resource::CreateTextureViewError::FormatReinterpretation {
1182                texture: texture.desc.format,
1183                view: resolved_format,
1184            });
1185        }
1186
1187        // check if multisampled texture is seen as anything but 2D
1188        if texture.desc.sample_count > 1 && resolved_dimension != TextureViewDimension::D2 {
1189            return Err(
1190                resource::CreateTextureViewError::InvalidMultisampledTextureViewDimension(
1191                    resolved_dimension,
1192                ),
1193            );
1194        }
1195
1196        // check if the dimension is compatible with the texture
1197        if texture.desc.dimension != resolved_dimension.compatible_texture_dimension() {
1198            return Err(
1199                resource::CreateTextureViewError::InvalidTextureViewDimension {
1200                    view: resolved_dimension,
1201                    texture: texture.desc.dimension,
1202                },
1203            );
1204        }
1205
1206        match resolved_dimension {
1207            TextureViewDimension::D1 | TextureViewDimension::D2 | TextureViewDimension::D3 => {
1208                if resolved_array_layer_count != 1 {
1209                    return Err(resource::CreateTextureViewError::InvalidArrayLayerCount {
1210                        requested: resolved_array_layer_count,
1211                        dim: resolved_dimension,
1212                    });
1213                }
1214            }
1215            TextureViewDimension::Cube => {
1216                if resolved_array_layer_count != 6 {
1217                    return Err(
1218                        resource::CreateTextureViewError::InvalidCubemapTextureDepth {
1219                            depth: resolved_array_layer_count,
1220                        },
1221                    );
1222                }
1223            }
1224            TextureViewDimension::CubeArray => {
1225                if resolved_array_layer_count % 6 != 0 {
1226                    return Err(
1227                        resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth {
1228                            depth: resolved_array_layer_count,
1229                        },
1230                    );
1231                }
1232            }
1233            _ => {}
1234        }
1235
1236        match resolved_dimension {
1237            TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1238                if texture.desc.size.width != texture.desc.size.height {
1239                    return Err(resource::CreateTextureViewError::InvalidCubeTextureViewSize);
1240                }
1241            }
1242            _ => {}
1243        }
1244
1245        if resolved_mip_level_count == 0 {
1246            return Err(resource::CreateTextureViewError::ZeroMipLevelCount);
1247        }
1248
1249        let mip_level_end = desc
1250            .range
1251            .base_mip_level
1252            .saturating_add(resolved_mip_level_count);
1253
1254        let level_end = texture.desc.mip_level_count;
1255        if mip_level_end > level_end {
1256            return Err(resource::CreateTextureViewError::TooManyMipLevels {
1257                requested: mip_level_end,
1258                total: level_end,
1259            });
1260        }
1261
1262        if resolved_array_layer_count == 0 {
1263            return Err(resource::CreateTextureViewError::ZeroArrayLayerCount);
1264        }
1265
1266        let array_layer_end = desc
1267            .range
1268            .base_array_layer
1269            .saturating_add(resolved_array_layer_count);
1270
1271        let layer_end = texture.desc.array_layer_count();
1272        if array_layer_end > layer_end {
1273            return Err(resource::CreateTextureViewError::TooManyArrayLayers {
1274                requested: array_layer_end,
1275                total: layer_end,
1276            });
1277        };
1278
1279        // https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view
1280        let render_extent = 'error: {
1281            if !texture
1282                .desc
1283                .usage
1284                .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1285            {
1286                break 'error Err(TextureViewNotRenderableReason::Usage(texture.desc.usage));
1287            }
1288
1289            if !(resolved_dimension == TextureViewDimension::D2
1290                || (self.features.contains(wgt::Features::MULTIVIEW)
1291                    && resolved_dimension == TextureViewDimension::D2Array))
1292            {
1293                break 'error Err(TextureViewNotRenderableReason::Dimension(
1294                    resolved_dimension,
1295                ));
1296            }
1297
1298            if resolved_mip_level_count != 1 {
1299                break 'error Err(TextureViewNotRenderableReason::MipLevelCount(
1300                    resolved_mip_level_count,
1301                ));
1302            }
1303
1304            if resolved_array_layer_count != 1
1305                && !(self.features.contains(wgt::Features::MULTIVIEW))
1306            {
1307                break 'error Err(TextureViewNotRenderableReason::ArrayLayerCount(
1308                    resolved_array_layer_count,
1309                ));
1310            }
1311
1312            if aspects != hal::FormatAspects::from(texture.desc.format) {
1313                break 'error Err(TextureViewNotRenderableReason::Aspects(aspects));
1314            }
1315
1316            Ok(texture
1317                .desc
1318                .compute_render_extent(desc.range.base_mip_level))
1319        };
1320
1321        // filter the usages based on the other criteria
1322        let usage = {
1323            let mask_copy = !(hal::TextureUses::COPY_SRC | hal::TextureUses::COPY_DST);
1324            let mask_dimension = match resolved_dimension {
1325                TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1326                    hal::TextureUses::RESOURCE
1327                }
1328                TextureViewDimension::D3 => {
1329                    hal::TextureUses::RESOURCE
1330                        | hal::TextureUses::STORAGE_READ
1331                        | hal::TextureUses::STORAGE_READ_WRITE
1332                }
1333                _ => hal::TextureUses::all(),
1334            };
1335            let mask_mip_level = if resolved_mip_level_count == 1 {
1336                hal::TextureUses::all()
1337            } else {
1338                hal::TextureUses::RESOURCE
1339            };
1340            texture.hal_usage & mask_copy & mask_dimension & mask_mip_level
1341        };
1342
1343        // use the combined depth-stencil format for the view
1344        let format = if resolved_format.is_depth_stencil_component(texture.desc.format) {
1345            texture.desc.format
1346        } else {
1347            resolved_format
1348        };
1349
1350        let resolved_range = wgt::ImageSubresourceRange {
1351            aspect: desc.range.aspect,
1352            base_mip_level: desc.range.base_mip_level,
1353            mip_level_count: Some(resolved_mip_level_count),
1354            base_array_layer: desc.range.base_array_layer,
1355            array_layer_count: Some(resolved_array_layer_count),
1356        };
1357
1358        let hal_desc = hal::TextureViewDescriptor {
1359            label: desc.label.to_hal(self.instance_flags),
1360            format,
1361            dimension: resolved_dimension,
1362            usage,
1363            range: resolved_range,
1364        };
1365
1366        let raw = unsafe { self.raw().create_texture_view(texture_raw, &hal_desc) }
1367            .map_err(|e| self.handle_hal_error(e))?;
1368
1369        let selector = TextureSelector {
1370            mips: desc.range.base_mip_level..mip_level_end,
1371            layers: desc.range.base_array_layer..array_layer_end,
1372        };
1373
1374        let view = TextureView {
1375            raw: Snatchable::new(raw),
1376            parent: texture.clone(),
1377            device: self.clone(),
1378            desc: resource::HalTextureViewDescriptor {
1379                texture_format: texture.desc.format,
1380                format: resolved_format,
1381                dimension: resolved_dimension,
1382                range: resolved_range,
1383            },
1384            format_features: texture.format_features,
1385            render_extent,
1386            samples: texture.desc.sample_count,
1387            selector,
1388            label: desc.label.to_string(),
1389            tracking_data: TrackingData::new(self.tracker_indices.texture_views.clone()),
1390        };
1391
1392        let view = Arc::new(view);
1393
1394        {
1395            let mut views = texture.views.lock();
1396            views.push(Arc::downgrade(&view));
1397        }
1398
1399        Ok(view)
1400    }
1401
1402    pub(crate) fn create_sampler(
1403        self: &Arc<Self>,
1404        desc: &resource::SamplerDescriptor,
1405    ) -> Result<Arc<Sampler>, resource::CreateSamplerError> {
1406        self.check_is_valid()?;
1407
1408        if desc
1409            .address_modes
1410            .iter()
1411            .any(|am| am == &wgt::AddressMode::ClampToBorder)
1412        {
1413            self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER)?;
1414        }
1415
1416        if desc.border_color == Some(wgt::SamplerBorderColor::Zero) {
1417            self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;
1418        }
1419
1420        if desc.lod_min_clamp < 0.0 {
1421            return Err(resource::CreateSamplerError::InvalidLodMinClamp(
1422                desc.lod_min_clamp,
1423            ));
1424        }
1425        if desc.lod_max_clamp < desc.lod_min_clamp {
1426            return Err(resource::CreateSamplerError::InvalidLodMaxClamp {
1427                lod_min_clamp: desc.lod_min_clamp,
1428                lod_max_clamp: desc.lod_max_clamp,
1429            });
1430        }
1431
1432        if desc.anisotropy_clamp < 1 {
1433            return Err(resource::CreateSamplerError::InvalidAnisotropy(
1434                desc.anisotropy_clamp,
1435            ));
1436        }
1437
1438        if desc.anisotropy_clamp != 1 {
1439            if !matches!(desc.min_filter, wgt::FilterMode::Linear) {
1440                return Err(
1441                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1442                        filter_type: resource::SamplerFilterErrorType::MinFilter,
1443                        filter_mode: desc.min_filter,
1444                        anisotropic_clamp: desc.anisotropy_clamp,
1445                    },
1446                );
1447            }
1448            if !matches!(desc.mag_filter, wgt::FilterMode::Linear) {
1449                return Err(
1450                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1451                        filter_type: resource::SamplerFilterErrorType::MagFilter,
1452                        filter_mode: desc.mag_filter,
1453                        anisotropic_clamp: desc.anisotropy_clamp,
1454                    },
1455                );
1456            }
1457            if !matches!(desc.mipmap_filter, wgt::FilterMode::Linear) {
1458                return Err(
1459                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1460                        filter_type: resource::SamplerFilterErrorType::MipmapFilter,
1461                        filter_mode: desc.mipmap_filter,
1462                        anisotropic_clamp: desc.anisotropy_clamp,
1463                    },
1464                );
1465            }
1466        }
1467
1468        let anisotropy_clamp = if self
1469            .downlevel
1470            .flags
1471            .contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING)
1472        {
1473            // Clamp anisotropy clamp to [1, 16] per the wgpu-hal interface
1474            desc.anisotropy_clamp.min(16)
1475        } else {
1476            // If it isn't supported, set this unconditionally to 1
1477            1
1478        };
1479
1480        //TODO: check for wgt::DownlevelFlags::COMPARISON_SAMPLERS
1481
1482        let hal_desc = hal::SamplerDescriptor {
1483            label: desc.label.to_hal(self.instance_flags),
1484            address_modes: desc.address_modes,
1485            mag_filter: desc.mag_filter,
1486            min_filter: desc.min_filter,
1487            mipmap_filter: desc.mipmap_filter,
1488            lod_clamp: desc.lod_min_clamp..desc.lod_max_clamp,
1489            compare: desc.compare,
1490            anisotropy_clamp,
1491            border_color: desc.border_color,
1492        };
1493
1494        let raw = unsafe { self.raw().create_sampler(&hal_desc) }
1495            .map_err(|e| self.handle_hal_error(e))?;
1496
1497        let sampler = Sampler {
1498            raw: ManuallyDrop::new(raw),
1499            device: self.clone(),
1500            label: desc.label.to_string(),
1501            tracking_data: TrackingData::new(self.tracker_indices.samplers.clone()),
1502            comparison: desc.compare.is_some(),
1503            filtering: desc.min_filter == wgt::FilterMode::Linear
1504                || desc.mag_filter == wgt::FilterMode::Linear
1505                || desc.mipmap_filter == wgt::FilterMode::Linear,
1506        };
1507
1508        let sampler = Arc::new(sampler);
1509
1510        Ok(sampler)
1511    }
1512
1513    pub(crate) fn create_shader_module<'a>(
1514        self: &Arc<Self>,
1515        desc: &pipeline::ShaderModuleDescriptor<'a>,
1516        source: pipeline::ShaderModuleSource<'a>,
1517    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
1518        self.check_is_valid()?;
1519
1520        let (module, source) = match source {
1521            #[cfg(feature = "wgsl")]
1522            pipeline::ShaderModuleSource::Wgsl(code) => {
1523                profiling::scope!("naga::front::wgsl::parse_str");
1524                let module = naga::front::wgsl::parse_str(&code).map_err(|inner| {
1525                    pipeline::CreateShaderModuleError::Parsing(naga::error::ShaderError {
1526                        source: code.to_string(),
1527                        label: desc.label.as_ref().map(|l| l.to_string()),
1528                        inner: Box::new(inner),
1529                    })
1530                })?;
1531                (Cow::Owned(module), code.into_owned())
1532            }
1533            #[cfg(feature = "spirv")]
1534            pipeline::ShaderModuleSource::SpirV(spv, options) => {
1535                let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options);
1536                profiling::scope!("naga::front::spv::Frontend");
1537                let module = parser.parse().map_err(|inner| {
1538                    pipeline::CreateShaderModuleError::ParsingSpirV(naga::error::ShaderError {
1539                        source: String::new(),
1540                        label: desc.label.as_ref().map(|l| l.to_string()),
1541                        inner: Box::new(inner),
1542                    })
1543                })?;
1544                (Cow::Owned(module), String::new())
1545            }
1546            #[cfg(feature = "glsl")]
1547            pipeline::ShaderModuleSource::Glsl(code, options) => {
1548                let mut parser = naga::front::glsl::Frontend::default();
1549                profiling::scope!("naga::front::glsl::Frontend.parse");
1550                let module = parser.parse(&options, &code).map_err(|inner| {
1551                    pipeline::CreateShaderModuleError::ParsingGlsl(naga::error::ShaderError {
1552                        source: code.to_string(),
1553                        label: desc.label.as_ref().map(|l| l.to_string()),
1554                        inner: Box::new(inner),
1555                    })
1556                })?;
1557                (Cow::Owned(module), code.into_owned())
1558            }
1559            pipeline::ShaderModuleSource::Naga(module) => (module, String::new()),
1560            pipeline::ShaderModuleSource::Dummy(_) => panic!("found `ShaderModuleSource::Dummy`"),
1561        };
1562        for (_, var) in module.global_variables.iter() {
1563            match var.binding {
1564                Some(ref br) if br.group >= self.limits.max_bind_groups => {
1565                    return Err(pipeline::CreateShaderModuleError::InvalidGroupIndex {
1566                        bind: br.clone(),
1567                        group: br.group,
1568                        limit: self.limits.max_bind_groups,
1569                    });
1570                }
1571                _ => continue,
1572            };
1573        }
1574
1575        profiling::scope!("naga::validate");
1576        let debug_source =
1577            if self.instance_flags.contains(wgt::InstanceFlags::DEBUG) && !source.is_empty() {
1578                Some(hal::DebugSource {
1579                    file_name: Cow::Owned(
1580                        desc.label
1581                            .as_ref()
1582                            .map_or("shader".to_string(), |l| l.to_string()),
1583                    ),
1584                    source_code: Cow::Owned(source.clone()),
1585                })
1586            } else {
1587                None
1588            };
1589
1590        let info = create_validator(
1591            self.features,
1592            self.downlevel.flags,
1593            naga::valid::ValidationFlags::all(),
1594        )
1595        .validate(&module)
1596        .map_err(|inner| {
1597            pipeline::CreateShaderModuleError::Validation(naga::error::ShaderError {
1598                source,
1599                label: desc.label.as_ref().map(|l| l.to_string()),
1600                inner: Box::new(inner),
1601            })
1602        })?;
1603
1604        let interface = validation::Interface::new(&module, &info, self.limits.clone());
1605        let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {
1606            module,
1607            info,
1608            debug_source,
1609        });
1610        let hal_desc = hal::ShaderModuleDescriptor {
1611            label: desc.label.to_hal(self.instance_flags),
1612            runtime_checks: desc.shader_bound_checks.runtime_checks(),
1613        };
1614        let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {
1615            Ok(raw) => raw,
1616            Err(error) => {
1617                return Err(match error {
1618                    hal::ShaderError::Device(error) => {
1619                        pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
1620                    }
1621                    hal::ShaderError::Compilation(ref msg) => {
1622                        log::error!("Shader error: {}", msg);
1623                        pipeline::CreateShaderModuleError::Generation
1624                    }
1625                })
1626            }
1627        };
1628
1629        let module = pipeline::ShaderModule {
1630            raw: ManuallyDrop::new(raw),
1631            device: self.clone(),
1632            interface: Some(interface),
1633            label: desc.label.to_string(),
1634        };
1635
1636        let module = Arc::new(module);
1637
1638        Ok(module)
1639    }
1640
1641    #[allow(unused_unsafe)]
1642    pub(crate) unsafe fn create_shader_module_spirv<'a>(
1643        self: &Arc<Self>,
1644        desc: &pipeline::ShaderModuleDescriptor<'a>,
1645        source: &'a [u32],
1646    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
1647        self.check_is_valid()?;
1648
1649        self.require_features(wgt::Features::SPIRV_SHADER_PASSTHROUGH)?;
1650        let hal_desc = hal::ShaderModuleDescriptor {
1651            label: desc.label.to_hal(self.instance_flags),
1652            runtime_checks: desc.shader_bound_checks.runtime_checks(),
1653        };
1654        let hal_shader = hal::ShaderInput::SpirV(source);
1655        let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {
1656            Ok(raw) => raw,
1657            Err(error) => {
1658                return Err(match error {
1659                    hal::ShaderError::Device(error) => {
1660                        pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
1661                    }
1662                    hal::ShaderError::Compilation(ref msg) => {
1663                        log::error!("Shader error: {}", msg);
1664                        pipeline::CreateShaderModuleError::Generation
1665                    }
1666                })
1667            }
1668        };
1669
1670        let module = pipeline::ShaderModule {
1671            raw: ManuallyDrop::new(raw),
1672            device: self.clone(),
1673            interface: None,
1674            label: desc.label.to_string(),
1675        };
1676
1677        let module = Arc::new(module);
1678
1679        Ok(module)
1680    }
1681
1682    pub(crate) fn create_command_encoder(
1683        self: &Arc<Self>,
1684        label: &crate::Label,
1685    ) -> Result<Arc<command::CommandBuffer>, DeviceError> {
1686        self.check_is_valid()?;
1687
1688        let queue = self.get_queue().unwrap();
1689
1690        let encoder = self
1691            .command_allocator
1692            .acquire_encoder(self.raw(), queue.raw())
1693            .map_err(|e| self.handle_hal_error(e))?;
1694
1695        let command_buffer = command::CommandBuffer::new(encoder, self, label);
1696
1697        let command_buffer = Arc::new(command_buffer);
1698
1699        Ok(command_buffer)
1700    }
1701
1702    /// Generate information about late-validated buffer bindings for pipelines.
1703    //TODO: should this be combined with `get_introspection_bind_group_layouts` in some way?
1704    fn make_late_sized_buffer_groups(
1705        shader_binding_sizes: &FastHashMap<naga::ResourceBinding, wgt::BufferSize>,
1706        layout: &binding_model::PipelineLayout,
1707    ) -> ArrayVec<pipeline::LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }> {
1708        // Given the shader-required binding sizes and the pipeline layout,
1709        // return the filtered list of them in the layout order,
1710        // removing those with given `min_binding_size`.
1711        layout
1712            .bind_group_layouts
1713            .iter()
1714            .enumerate()
1715            .map(|(group_index, bgl)| pipeline::LateSizedBufferGroup {
1716                shader_sizes: bgl
1717                    .entries
1718                    .values()
1719                    .filter_map(|entry| match entry.ty {
1720                        wgt::BindingType::Buffer {
1721                            min_binding_size: None,
1722                            ..
1723                        } => {
1724                            let rb = naga::ResourceBinding {
1725                                group: group_index as u32,
1726                                binding: entry.binding,
1727                            };
1728                            let shader_size =
1729                                shader_binding_sizes.get(&rb).map_or(0, |nz| nz.get());
1730                            Some(shader_size)
1731                        }
1732                        _ => None,
1733                    })
1734                    .collect(),
1735            })
1736            .collect()
1737    }
1738
1739    pub(crate) fn create_bind_group_layout(
1740        self: &Arc<Self>,
1741        label: &crate::Label,
1742        entry_map: bgl::EntryMap,
1743        origin: bgl::Origin,
1744    ) -> Result<Arc<BindGroupLayout>, binding_model::CreateBindGroupLayoutError> {
1745        #[derive(PartialEq)]
1746        enum WritableStorage {
1747            Yes,
1748            No,
1749        }
1750
1751        for entry in entry_map.values() {
1752            use wgt::BindingType as Bt;
1753
1754            let mut required_features = wgt::Features::empty();
1755            let mut required_downlevel_flags = wgt::DownlevelFlags::empty();
1756            let (array_feature, writable_storage) = match entry.ty {
1757                Bt::Buffer {
1758                    ty: wgt::BufferBindingType::Uniform,
1759                    has_dynamic_offset: false,
1760                    min_binding_size: _,
1761                } => (
1762                    Some(wgt::Features::BUFFER_BINDING_ARRAY),
1763                    WritableStorage::No,
1764                ),
1765                Bt::Buffer {
1766                    ty: wgt::BufferBindingType::Uniform,
1767                    has_dynamic_offset: true,
1768                    min_binding_size: _,
1769                } => (
1770                    Some(wgt::Features::BUFFER_BINDING_ARRAY),
1771                    WritableStorage::No,
1772                ),
1773                Bt::Buffer {
1774                    ty: wgt::BufferBindingType::Storage { read_only },
1775                    ..
1776                } => (
1777                    Some(
1778                        wgt::Features::BUFFER_BINDING_ARRAY
1779                            | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
1780                    ),
1781                    match read_only {
1782                        true => WritableStorage::No,
1783                        false => WritableStorage::Yes,
1784                    },
1785                ),
1786                Bt::Sampler { .. } => (
1787                    Some(wgt::Features::TEXTURE_BINDING_ARRAY),
1788                    WritableStorage::No,
1789                ),
1790                Bt::Texture {
1791                    multisampled: true,
1792                    sample_type: TextureSampleType::Float { filterable: true },
1793                    ..
1794                } => {
1795                    return Err(binding_model::CreateBindGroupLayoutError::Entry {
1796                        binding: entry.binding,
1797                        error:
1798                            BindGroupLayoutEntryError::SampleTypeFloatFilterableBindingMultisampled,
1799                    });
1800                }
1801                Bt::Texture {
1802                    multisampled,
1803                    view_dimension,
1804                    ..
1805                } => {
1806                    if multisampled && view_dimension != TextureViewDimension::D2 {
1807                        return Err(binding_model::CreateBindGroupLayoutError::Entry {
1808                            binding: entry.binding,
1809                            error: BindGroupLayoutEntryError::Non2DMultisampled(view_dimension),
1810                        });
1811                    }
1812
1813                    (
1814                        Some(wgt::Features::TEXTURE_BINDING_ARRAY),
1815                        WritableStorage::No,
1816                    )
1817                }
1818                Bt::StorageTexture {
1819                    access,
1820                    view_dimension,
1821                    format: _,
1822                } => {
1823                    match view_dimension {
1824                        TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1825                            return Err(binding_model::CreateBindGroupLayoutError::Entry {
1826                                binding: entry.binding,
1827                                error: BindGroupLayoutEntryError::StorageTextureCube,
1828                            })
1829                        }
1830                        _ => (),
1831                    }
1832                    match access {
1833                        wgt::StorageTextureAccess::ReadOnly
1834                        | wgt::StorageTextureAccess::ReadWrite
1835                            if !self.features.contains(
1836                                wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
1837                            ) =>
1838                        {
1839                            return Err(binding_model::CreateBindGroupLayoutError::Entry {
1840                                binding: entry.binding,
1841                                error: BindGroupLayoutEntryError::StorageTextureReadWrite,
1842                            });
1843                        }
1844                        _ => (),
1845                    }
1846                    (
1847                        Some(
1848                            wgt::Features::TEXTURE_BINDING_ARRAY
1849                                | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
1850                        ),
1851                        match access {
1852                            wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes,
1853                            wgt::StorageTextureAccess::ReadOnly => {
1854                                required_features |=
1855                                    wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
1856                                WritableStorage::No
1857                            }
1858                            wgt::StorageTextureAccess::ReadWrite => {
1859                                required_features |=
1860                                    wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
1861                                WritableStorage::Yes
1862                            }
1863                        },
1864                    )
1865                }
1866                Bt::AccelerationStructure => todo!(),
1867            };
1868
1869            // Validate the count parameter
1870            if entry.count.is_some() {
1871                required_features |= array_feature
1872                    .ok_or(BindGroupLayoutEntryError::ArrayUnsupported)
1873                    .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
1874                        binding: entry.binding,
1875                        error,
1876                    })?;
1877            }
1878
1879            if entry.visibility.contains_invalid_bits() {
1880                return Err(
1881                    binding_model::CreateBindGroupLayoutError::InvalidVisibility(entry.visibility),
1882                );
1883            }
1884
1885            if entry.visibility.contains(wgt::ShaderStages::VERTEX) {
1886                if writable_storage == WritableStorage::Yes {
1887                    required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
1888                }
1889                if let Bt::Buffer {
1890                    ty: wgt::BufferBindingType::Storage { .. },
1891                    ..
1892                } = entry.ty
1893                {
1894                    required_downlevel_flags |= wgt::DownlevelFlags::VERTEX_STORAGE;
1895                }
1896            }
1897            if writable_storage == WritableStorage::Yes
1898                && entry.visibility.contains(wgt::ShaderStages::FRAGMENT)
1899            {
1900                required_downlevel_flags |= wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE;
1901            }
1902
1903            self.require_features(required_features)
1904                .map_err(BindGroupLayoutEntryError::MissingFeatures)
1905                .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
1906                    binding: entry.binding,
1907                    error,
1908                })?;
1909            self.require_downlevel_flags(required_downlevel_flags)
1910                .map_err(BindGroupLayoutEntryError::MissingDownlevelFlags)
1911                .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
1912                    binding: entry.binding,
1913                    error,
1914                })?;
1915        }
1916
1917        let bgl_flags = conv::bind_group_layout_flags(self.features);
1918
1919        let hal_bindings = entry_map.values().copied().collect::<Vec<_>>();
1920        let hal_desc = hal::BindGroupLayoutDescriptor {
1921            label: label.to_hal(self.instance_flags),
1922            flags: bgl_flags,
1923            entries: &hal_bindings,
1924        };
1925
1926        let raw = unsafe { self.raw().create_bind_group_layout(&hal_desc) }
1927            .map_err(|e| self.handle_hal_error(e))?;
1928
1929        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
1930        for entry in entry_map.values() {
1931            count_validator.add_binding(entry);
1932        }
1933        // If a single bind group layout violates limits, the pipeline layout is
1934        // definitely going to violate limits too, lets catch it now.
1935        count_validator
1936            .validate(&self.limits)
1937            .map_err(binding_model::CreateBindGroupLayoutError::TooManyBindings)?;
1938
1939        let bgl = BindGroupLayout {
1940            raw: ManuallyDrop::new(raw),
1941            device: self.clone(),
1942            entries: entry_map,
1943            origin,
1944            exclusive_pipeline: OnceLock::new(),
1945            binding_count_validator: count_validator,
1946            label: label.to_string(),
1947        };
1948
1949        let bgl = Arc::new(bgl);
1950
1951        Ok(bgl)
1952    }
1953
1954    fn create_buffer_binding<'a>(
1955        &self,
1956        bb: &'a binding_model::ResolvedBufferBinding,
1957        binding: u32,
1958        decl: &wgt::BindGroupLayoutEntry,
1959        used_buffer_ranges: &mut Vec<BufferInitTrackerAction>,
1960        dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
1961        late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
1962        used: &mut BindGroupStates,
1963        snatch_guard: &'a SnatchGuard<'a>,
1964    ) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, binding_model::CreateBindGroupError>
1965    {
1966        use crate::binding_model::CreateBindGroupError as Error;
1967
1968        let (binding_ty, dynamic, min_size) = match decl.ty {
1969            wgt::BindingType::Buffer {
1970                ty,
1971                has_dynamic_offset,
1972                min_binding_size,
1973            } => (ty, has_dynamic_offset, min_binding_size),
1974            _ => {
1975                return Err(Error::WrongBindingType {
1976                    binding,
1977                    actual: decl.ty,
1978                    expected: "UniformBuffer, StorageBuffer or ReadonlyStorageBuffer",
1979                })
1980            }
1981        };
1982
1983        let (pub_usage, internal_use, range_limit) = match binding_ty {
1984            wgt::BufferBindingType::Uniform => (
1985                wgt::BufferUsages::UNIFORM,
1986                hal::BufferUses::UNIFORM,
1987                self.limits.max_uniform_buffer_binding_size,
1988            ),
1989            wgt::BufferBindingType::Storage { read_only } => (
1990                wgt::BufferUsages::STORAGE,
1991                if read_only {
1992                    hal::BufferUses::STORAGE_READ
1993                } else {
1994                    hal::BufferUses::STORAGE_READ_WRITE
1995                },
1996                self.limits.max_storage_buffer_binding_size,
1997            ),
1998        };
1999
2000        let (align, align_limit_name) =
2001            binding_model::buffer_binding_type_alignment(&self.limits, binding_ty);
2002        if bb.offset % align as u64 != 0 {
2003            return Err(Error::UnalignedBufferOffset(
2004                bb.offset,
2005                align_limit_name,
2006                align,
2007            ));
2008        }
2009
2010        let buffer = &bb.buffer;
2011
2012        used.buffers.insert_single(buffer.clone(), internal_use);
2013
2014        buffer.same_device(self)?;
2015
2016        buffer.check_usage(pub_usage)?;
2017        let raw_buffer = buffer.try_raw(snatch_guard)?;
2018
2019        let (bind_size, bind_end) = match bb.size {
2020            Some(size) => {
2021                let end = bb.offset + size.get();
2022                if end > buffer.size {
2023                    return Err(Error::BindingRangeTooLarge {
2024                        buffer: buffer.error_ident(),
2025                        range: bb.offset..end,
2026                        size: buffer.size,
2027                    });
2028                }
2029                (size.get(), end)
2030            }
2031            None => {
2032                if buffer.size < bb.offset {
2033                    return Err(Error::BindingRangeTooLarge {
2034                        buffer: buffer.error_ident(),
2035                        range: bb.offset..bb.offset,
2036                        size: buffer.size,
2037                    });
2038                }
2039                (buffer.size - bb.offset, buffer.size)
2040            }
2041        };
2042
2043        if bind_size > range_limit as u64 {
2044            return Err(Error::BufferRangeTooLarge {
2045                binding,
2046                given: bind_size as u32,
2047                limit: range_limit,
2048            });
2049        }
2050
2051        // Record binding info for validating dynamic offsets
2052        if dynamic {
2053            dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
2054                binding_idx: binding,
2055                buffer_size: buffer.size,
2056                binding_range: bb.offset..bind_end,
2057                maximum_dynamic_offset: buffer.size - bind_end,
2058                binding_type: binding_ty,
2059            });
2060        }
2061
2062        if let Some(non_zero) = min_size {
2063            let min_size = non_zero.get();
2064            if min_size > bind_size {
2065                return Err(Error::BindingSizeTooSmall {
2066                    buffer: buffer.error_ident(),
2067                    actual: bind_size,
2068                    min: min_size,
2069                });
2070            }
2071        } else {
2072            let late_size = wgt::BufferSize::new(bind_size)
2073                .ok_or_else(|| Error::BindingZeroSize(buffer.error_ident()))?;
2074            late_buffer_binding_sizes.insert(binding, late_size);
2075        }
2076
2077        // This was checked against the device's alignment requirements above,
2078        // which should always be a multiple of `COPY_BUFFER_ALIGNMENT`.
2079        assert_eq!(bb.offset % wgt::COPY_BUFFER_ALIGNMENT, 0);
2080
2081        // `wgpu_hal` only restricts shader access to bound buffer regions with
2082        // a certain resolution. For the sake of lazy initialization, round up
2083        // the size of the bound range to reflect how much of the buffer is
2084        // actually going to be visible to the shader.
2085        let bounds_check_alignment =
2086            binding_model::buffer_binding_type_bounds_check_alignment(&self.alignments, binding_ty);
2087        let visible_size = align_to(bind_size, bounds_check_alignment);
2088
2089        used_buffer_ranges.extend(buffer.initialization_status.read().create_action(
2090            buffer,
2091            bb.offset..bb.offset + visible_size,
2092            MemoryInitKind::NeedsInitializedMemory,
2093        ));
2094
2095        Ok(hal::BufferBinding {
2096            buffer: raw_buffer,
2097            offset: bb.offset,
2098            size: bb.size,
2099        })
2100    }
2101
2102    fn create_sampler_binding<'a>(
2103        &self,
2104        used: &mut BindGroupStates,
2105        binding: u32,
2106        decl: &wgt::BindGroupLayoutEntry,
2107        sampler: &'a Arc<Sampler>,
2108    ) -> Result<&'a dyn hal::DynSampler, binding_model::CreateBindGroupError> {
2109        use crate::binding_model::CreateBindGroupError as Error;
2110
2111        used.samplers.insert_single(sampler.clone());
2112
2113        sampler.same_device(self)?;
2114
2115        match decl.ty {
2116            wgt::BindingType::Sampler(ty) => {
2117                let (allowed_filtering, allowed_comparison) = match ty {
2118                    wgt::SamplerBindingType::Filtering => (None, false),
2119                    wgt::SamplerBindingType::NonFiltering => (Some(false), false),
2120                    wgt::SamplerBindingType::Comparison => (None, true),
2121                };
2122                if let Some(allowed_filtering) = allowed_filtering {
2123                    if allowed_filtering != sampler.filtering {
2124                        return Err(Error::WrongSamplerFiltering {
2125                            binding,
2126                            layout_flt: allowed_filtering,
2127                            sampler_flt: sampler.filtering,
2128                        });
2129                    }
2130                }
2131                if allowed_comparison != sampler.comparison {
2132                    return Err(Error::WrongSamplerComparison {
2133                        binding,
2134                        layout_cmp: allowed_comparison,
2135                        sampler_cmp: sampler.comparison,
2136                    });
2137                }
2138            }
2139            _ => {
2140                return Err(Error::WrongBindingType {
2141                    binding,
2142                    actual: decl.ty,
2143                    expected: "Sampler",
2144                })
2145            }
2146        }
2147
2148        Ok(sampler.raw())
2149    }
2150
2151    fn create_texture_binding<'a>(
2152        &self,
2153        binding: u32,
2154        decl: &wgt::BindGroupLayoutEntry,
2155        view: &'a Arc<TextureView>,
2156        used: &mut BindGroupStates,
2157        used_texture_ranges: &mut Vec<TextureInitTrackerAction>,
2158        snatch_guard: &'a SnatchGuard<'a>,
2159    ) -> Result<hal::TextureBinding<'a, dyn hal::DynTextureView>, binding_model::CreateBindGroupError>
2160    {
2161        view.same_device(self)?;
2162
2163        let (pub_usage, internal_use) = self.texture_use_parameters(
2164            binding,
2165            decl,
2166            view,
2167            "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
2168        )?;
2169
2170        used.views.insert_single(view.clone(), internal_use);
2171
2172        let texture = &view.parent;
2173        texture.check_usage(pub_usage)?;
2174
2175        used_texture_ranges.push(TextureInitTrackerAction {
2176            texture: texture.clone(),
2177            range: TextureInitRange {
2178                mip_range: view.desc.range.mip_range(texture.desc.mip_level_count),
2179                layer_range: view
2180                    .desc
2181                    .range
2182                    .layer_range(texture.desc.array_layer_count()),
2183            },
2184            kind: MemoryInitKind::NeedsInitializedMemory,
2185        });
2186
2187        Ok(hal::TextureBinding {
2188            view: view.try_raw(snatch_guard)?,
2189            usage: internal_use,
2190        })
2191    }
2192
2193    // This function expects the provided bind group layout to be resolved
2194    // (not passing a duplicate) beforehand.
2195    pub(crate) fn create_bind_group(
2196        self: &Arc<Self>,
2197        desc: binding_model::ResolvedBindGroupDescriptor,
2198    ) -> Result<Arc<BindGroup>, binding_model::CreateBindGroupError> {
2199        use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};
2200
2201        let layout = desc.layout;
2202
2203        self.check_is_valid()?;
2204        layout.same_device(self)?;
2205
2206        {
2207            // Check that the number of entries in the descriptor matches
2208            // the number of entries in the layout.
2209            let actual = desc.entries.len();
2210            let expected = layout.entries.len();
2211            if actual != expected {
2212                return Err(Error::BindingsNumMismatch { expected, actual });
2213            }
2214        }
2215
2216        // TODO: arrayvec/smallvec, or re-use allocations
2217        // Record binding info for dynamic offset validation
2218        let mut dynamic_binding_info = Vec::new();
2219        // Map of binding -> shader reflected size
2220        //Note: we can't collect into a vector right away because
2221        // it needs to be in BGL iteration order, not BG entry order.
2222        let mut late_buffer_binding_sizes = FastHashMap::default();
2223        // fill out the descriptors
2224        let mut used = BindGroupStates::new();
2225
2226        let mut used_buffer_ranges = Vec::new();
2227        let mut used_texture_ranges = Vec::new();
2228        let mut hal_entries = Vec::with_capacity(desc.entries.len());
2229        let mut hal_buffers = Vec::new();
2230        let mut hal_samplers = Vec::new();
2231        let mut hal_textures = Vec::new();
2232        let snatch_guard = self.snatchable_lock.read();
2233        for entry in desc.entries.iter() {
2234            let binding = entry.binding;
2235            // Find the corresponding declaration in the layout
2236            let decl = layout
2237                .entries
2238                .get(binding)
2239                .ok_or(Error::MissingBindingDeclaration(binding))?;
2240            let (res_index, count) = match entry.resource {
2241                Br::Buffer(ref bb) => {
2242                    let bb = self.create_buffer_binding(
2243                        bb,
2244                        binding,
2245                        decl,
2246                        &mut used_buffer_ranges,
2247                        &mut dynamic_binding_info,
2248                        &mut late_buffer_binding_sizes,
2249                        &mut used,
2250                        &snatch_guard,
2251                    )?;
2252
2253                    let res_index = hal_buffers.len();
2254                    hal_buffers.push(bb);
2255                    (res_index, 1)
2256                }
2257                Br::BufferArray(ref bindings_array) => {
2258                    let num_bindings = bindings_array.len();
2259                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
2260
2261                    let res_index = hal_buffers.len();
2262                    for bb in bindings_array.iter() {
2263                        let bb = self.create_buffer_binding(
2264                            bb,
2265                            binding,
2266                            decl,
2267                            &mut used_buffer_ranges,
2268                            &mut dynamic_binding_info,
2269                            &mut late_buffer_binding_sizes,
2270                            &mut used,
2271                            &snatch_guard,
2272                        )?;
2273                        hal_buffers.push(bb);
2274                    }
2275                    (res_index, num_bindings)
2276                }
2277                Br::Sampler(ref sampler) => {
2278                    let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;
2279
2280                    let res_index = hal_samplers.len();
2281                    hal_samplers.push(sampler);
2282                    (res_index, 1)
2283                }
2284                Br::SamplerArray(ref samplers) => {
2285                    let num_bindings = samplers.len();
2286                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
2287
2288                    let res_index = hal_samplers.len();
2289                    for sampler in samplers.iter() {
2290                        let sampler =
2291                            self.create_sampler_binding(&mut used, binding, decl, sampler)?;
2292
2293                        hal_samplers.push(sampler);
2294                    }
2295
2296                    (res_index, num_bindings)
2297                }
2298                Br::TextureView(ref view) => {
2299                    let tb = self.create_texture_binding(
2300                        binding,
2301                        decl,
2302                        view,
2303                        &mut used,
2304                        &mut used_texture_ranges,
2305                        &snatch_guard,
2306                    )?;
2307                    let res_index = hal_textures.len();
2308                    hal_textures.push(tb);
2309                    (res_index, 1)
2310                }
2311                Br::TextureViewArray(ref views) => {
2312                    let num_bindings = views.len();
2313                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
2314
2315                    let res_index = hal_textures.len();
2316                    for view in views.iter() {
2317                        let tb = self.create_texture_binding(
2318                            binding,
2319                            decl,
2320                            view,
2321                            &mut used,
2322                            &mut used_texture_ranges,
2323                            &snatch_guard,
2324                        )?;
2325
2326                        hal_textures.push(tb);
2327                    }
2328
2329                    (res_index, num_bindings)
2330                }
2331            };
2332
2333            hal_entries.push(hal::BindGroupEntry {
2334                binding,
2335                resource_index: res_index as u32,
2336                count: count as u32,
2337            });
2338        }
2339
2340        used.optimize();
2341
2342        hal_entries.sort_by_key(|entry| entry.binding);
2343        for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
2344            if a.binding == b.binding {
2345                return Err(Error::DuplicateBinding(a.binding));
2346            }
2347        }
2348        let hal_desc = hal::BindGroupDescriptor {
2349            label: desc.label.to_hal(self.instance_flags),
2350            layout: layout.raw(),
2351            entries: &hal_entries,
2352            buffers: &hal_buffers,
2353            samplers: &hal_samplers,
2354            textures: &hal_textures,
2355            acceleration_structures: &[],
2356        };
2357        let raw = unsafe { self.raw().create_bind_group(&hal_desc) }
2358            .map_err(|e| self.handle_hal_error(e))?;
2359
2360        // collect in the order of BGL iteration
2361        let late_buffer_binding_sizes = layout
2362            .entries
2363            .indices()
2364            .flat_map(|binding| late_buffer_binding_sizes.get(&binding).cloned())
2365            .collect();
2366
2367        let bind_group = BindGroup {
2368            raw: Snatchable::new(raw),
2369            device: self.clone(),
2370            layout,
2371            label: desc.label.to_string(),
2372            tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),
2373            used,
2374            used_buffer_ranges,
2375            used_texture_ranges,
2376            dynamic_binding_info,
2377            late_buffer_binding_sizes,
2378        };
2379
2380        let bind_group = Arc::new(bind_group);
2381
2382        let weak_ref = Arc::downgrade(&bind_group);
2383        for range in &bind_group.used_texture_ranges {
2384            let mut bind_groups = range.texture.bind_groups.lock();
2385            bind_groups.push(weak_ref.clone());
2386        }
2387        for range in &bind_group.used_buffer_ranges {
2388            let mut bind_groups = range.buffer.bind_groups.lock();
2389            bind_groups.push(weak_ref.clone());
2390        }
2391
2392        Ok(bind_group)
2393    }
2394
2395    fn check_array_binding(
2396        features: wgt::Features,
2397        count: Option<NonZeroU32>,
2398        num_bindings: usize,
2399    ) -> Result<(), binding_model::CreateBindGroupError> {
2400        use super::binding_model::CreateBindGroupError as Error;
2401
2402        if let Some(count) = count {
2403            let count = count.get() as usize;
2404            if count < num_bindings {
2405                return Err(Error::BindingArrayPartialLengthMismatch {
2406                    actual: num_bindings,
2407                    expected: count,
2408                });
2409            }
2410            if count != num_bindings
2411                && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
2412            {
2413                return Err(Error::BindingArrayLengthMismatch {
2414                    actual: num_bindings,
2415                    expected: count,
2416                });
2417            }
2418            if num_bindings == 0 {
2419                return Err(Error::BindingArrayZeroLength);
2420            }
2421        } else {
2422            return Err(Error::SingleBindingExpected);
2423        };
2424
2425        Ok(())
2426    }
2427
2428    fn texture_use_parameters(
2429        &self,
2430        binding: u32,
2431        decl: &wgt::BindGroupLayoutEntry,
2432        view: &TextureView,
2433        expected: &'static str,
2434    ) -> Result<(wgt::TextureUsages, hal::TextureUses), binding_model::CreateBindGroupError> {
2435        use crate::binding_model::CreateBindGroupError as Error;
2436        if view
2437            .desc
2438            .aspects()
2439            .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
2440        {
2441            return Err(Error::DepthStencilAspect);
2442        }
2443        match decl.ty {
2444            wgt::BindingType::Texture {
2445                sample_type,
2446                view_dimension,
2447                multisampled,
2448            } => {
2449                use wgt::TextureSampleType as Tst;
2450                if multisampled != (view.samples != 1) {
2451                    return Err(Error::InvalidTextureMultisample {
2452                        binding,
2453                        layout_multisampled: multisampled,
2454                        view_samples: view.samples,
2455                    });
2456                }
2457                let compat_sample_type = view
2458                    .desc
2459                    .format
2460                    .sample_type(Some(view.desc.range.aspect), Some(self.features))
2461                    .unwrap();
2462                match (sample_type, compat_sample_type) {
2463                    (Tst::Uint, Tst::Uint) |
2464                        (Tst::Sint, Tst::Sint) |
2465                        (Tst::Depth, Tst::Depth) |
2466                        // if we expect non-filterable, accept anything float
2467                        (Tst::Float { filterable: false }, Tst::Float { .. }) |
2468                        // if we expect filterable, require it
2469                        (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
2470                        // if we expect non-filterable, also accept depth
2471                        (Tst::Float { filterable: false }, Tst::Depth) => {}
2472                    // if we expect filterable, also accept Float that is defined as
2473                    // unfilterable if filterable feature is explicitly enabled (only hit
2474                    // if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is
2475                    // enabled)
2476                    (Tst::Float { filterable: true }, Tst::Float { .. }) if view.format_features.flags.contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
2477                    _ => {
2478                        return Err(Error::InvalidTextureSampleType {
2479                            binding,
2480                            layout_sample_type: sample_type,
2481                            view_format: view.desc.format,
2482                        })
2483                    }
2484                }
2485                if view_dimension != view.desc.dimension {
2486                    return Err(Error::InvalidTextureDimension {
2487                        binding,
2488                        layout_dimension: view_dimension,
2489                        view_dimension: view.desc.dimension,
2490                    });
2491                }
2492                Ok((
2493                    wgt::TextureUsages::TEXTURE_BINDING,
2494                    hal::TextureUses::RESOURCE,
2495                ))
2496            }
2497            wgt::BindingType::StorageTexture {
2498                access,
2499                format,
2500                view_dimension,
2501            } => {
2502                if format != view.desc.format {
2503                    return Err(Error::InvalidStorageTextureFormat {
2504                        binding,
2505                        layout_format: format,
2506                        view_format: view.desc.format,
2507                    });
2508                }
2509                if view_dimension != view.desc.dimension {
2510                    return Err(Error::InvalidTextureDimension {
2511                        binding,
2512                        layout_dimension: view_dimension,
2513                        view_dimension: view.desc.dimension,
2514                    });
2515                }
2516
2517                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
2518                if mip_level_count != 1 {
2519                    return Err(Error::InvalidStorageTextureMipLevelCount {
2520                        binding,
2521                        mip_level_count,
2522                    });
2523                }
2524
2525                let internal_use = match access {
2526                    wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_READ_WRITE,
2527                    wgt::StorageTextureAccess::ReadOnly => {
2528                        if !view
2529                            .format_features
2530                            .flags
2531                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE)
2532                        {
2533                            return Err(Error::StorageReadNotSupported(view.desc.format));
2534                        }
2535                        hal::TextureUses::STORAGE_READ
2536                    }
2537                    wgt::StorageTextureAccess::ReadWrite => {
2538                        if !view
2539                            .format_features
2540                            .flags
2541                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE)
2542                        {
2543                            return Err(Error::StorageReadNotSupported(view.desc.format));
2544                        }
2545
2546                        hal::TextureUses::STORAGE_READ_WRITE
2547                    }
2548                };
2549                Ok((wgt::TextureUsages::STORAGE_BINDING, internal_use))
2550            }
2551            _ => Err(Error::WrongBindingType {
2552                binding,
2553                actual: decl.ty,
2554                expected,
2555            }),
2556        }
2557    }
2558
2559    pub(crate) fn create_pipeline_layout(
2560        self: &Arc<Self>,
2561        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
2562    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
2563        use crate::binding_model::CreatePipelineLayoutError as Error;
2564
2565        self.check_is_valid()?;
2566
2567        let bind_group_layouts_count = desc.bind_group_layouts.len();
2568        let device_max_bind_groups = self.limits.max_bind_groups as usize;
2569        if bind_group_layouts_count > device_max_bind_groups {
2570            return Err(Error::TooManyGroups {
2571                actual: bind_group_layouts_count,
2572                max: device_max_bind_groups,
2573            });
2574        }
2575
2576        if !desc.push_constant_ranges.is_empty() {
2577            self.require_features(wgt::Features::PUSH_CONSTANTS)?;
2578        }
2579
2580        let mut used_stages = wgt::ShaderStages::empty();
2581        for (index, pc) in desc.push_constant_ranges.iter().enumerate() {
2582            if pc.stages.intersects(used_stages) {
2583                return Err(Error::MoreThanOnePushConstantRangePerStage {
2584                    index,
2585                    provided: pc.stages,
2586                    intersected: pc.stages & used_stages,
2587                });
2588            }
2589            used_stages |= pc.stages;
2590
2591            let device_max_pc_size = self.limits.max_push_constant_size;
2592            if device_max_pc_size < pc.range.end {
2593                return Err(Error::PushConstantRangeTooLarge {
2594                    index,
2595                    range: pc.range.clone(),
2596                    max: device_max_pc_size,
2597                });
2598            }
2599
2600            if pc.range.start % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
2601                return Err(Error::MisalignedPushConstantRange {
2602                    index,
2603                    bound: pc.range.start,
2604                });
2605            }
2606            if pc.range.end % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
2607                return Err(Error::MisalignedPushConstantRange {
2608                    index,
2609                    bound: pc.range.end,
2610                });
2611            }
2612        }
2613
2614        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
2615
2616        for bgl in desc.bind_group_layouts.iter() {
2617            bgl.same_device(self)?;
2618            count_validator.merge(&bgl.binding_count_validator);
2619        }
2620
2621        count_validator
2622            .validate(&self.limits)
2623            .map_err(Error::TooManyBindings)?;
2624
2625        let bind_group_layouts = desc
2626            .bind_group_layouts
2627            .iter()
2628            .cloned()
2629            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
2630
2631        let raw_bind_group_layouts = desc
2632            .bind_group_layouts
2633            .iter()
2634            .map(|bgl| bgl.raw())
2635            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
2636
2637        let hal_desc = hal::PipelineLayoutDescriptor {
2638            label: desc.label.to_hal(self.instance_flags),
2639            flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
2640                | hal::PipelineLayoutFlags::NUM_WORK_GROUPS,
2641            bind_group_layouts: &raw_bind_group_layouts,
2642            push_constant_ranges: desc.push_constant_ranges.as_ref(),
2643        };
2644
2645        let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }
2646            .map_err(|e| self.handle_hal_error(e))?;
2647
2648        drop(raw_bind_group_layouts);
2649
2650        let layout = binding_model::PipelineLayout {
2651            raw: ManuallyDrop::new(raw),
2652            device: self.clone(),
2653            label: desc.label.to_string(),
2654            bind_group_layouts,
2655            push_constant_ranges: desc.push_constant_ranges.iter().cloned().collect(),
2656        };
2657
2658        let layout = Arc::new(layout);
2659
2660        Ok(layout)
2661    }
2662
2663    pub(crate) fn derive_pipeline_layout(
2664        self: &Arc<Self>,
2665        mut derived_group_layouts: ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>,
2666    ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {
2667        while derived_group_layouts
2668            .last()
2669            .map_or(false, |map| map.is_empty())
2670        {
2671            derived_group_layouts.pop();
2672        }
2673
2674        let mut unique_bind_group_layouts = PreHashedMap::default();
2675
2676        let bind_group_layouts = derived_group_layouts
2677            .into_iter()
2678            .map(|mut bgl_entry_map| {
2679                bgl_entry_map.sort();
2680                match unique_bind_group_layouts.entry(PreHashedKey::from_key(&bgl_entry_map)) {
2681                    std::collections::hash_map::Entry::Occupied(v) => Ok(Arc::clone(v.get())),
2682                    std::collections::hash_map::Entry::Vacant(e) => {
2683                        match self.create_bind_group_layout(
2684                            &None,
2685                            bgl_entry_map,
2686                            bgl::Origin::Derived,
2687                        ) {
2688                            Ok(bgl) => {
2689                                e.insert(bgl.clone());
2690                                Ok(bgl)
2691                            }
2692                            Err(e) => Err(e),
2693                        }
2694                    }
2695                }
2696            })
2697            .collect::<Result<Vec<_>, _>>()?;
2698
2699        let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {
2700            label: None,
2701            bind_group_layouts: Cow::Owned(bind_group_layouts),
2702            push_constant_ranges: Cow::Borrowed(&[]), //TODO?
2703        };
2704
2705        let layout = self.create_pipeline_layout(&layout_desc)?;
2706        Ok(layout)
2707    }
2708
2709    pub(crate) fn create_compute_pipeline(
2710        self: &Arc<Self>,
2711        desc: pipeline::ResolvedComputePipelineDescriptor,
2712    ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {
2713        self.check_is_valid()?;
2714
2715        self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
2716
2717        let shader_module = desc.stage.module;
2718
2719        shader_module.same_device(self)?;
2720
2721        let is_auto_layout = desc.layout.is_none();
2722
2723        // Get the pipeline layout from the desc if it is provided.
2724        let pipeline_layout = match desc.layout {
2725            Some(pipeline_layout) => {
2726                pipeline_layout.same_device(self)?;
2727                Some(pipeline_layout)
2728            }
2729            None => None,
2730        };
2731
2732        let mut binding_layout_source = match pipeline_layout {
2733            Some(ref pipeline_layout) => {
2734                validation::BindingLayoutSource::Provided(pipeline_layout.get_binding_maps())
2735            }
2736            None => validation::BindingLayoutSource::new_derived(&self.limits),
2737        };
2738        let mut shader_binding_sizes = FastHashMap::default();
2739        let io = validation::StageIo::default();
2740
2741        let final_entry_point_name;
2742
2743        {
2744            let stage = wgt::ShaderStages::COMPUTE;
2745
2746            final_entry_point_name = shader_module.finalize_entry_point_name(
2747                stage,
2748                desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
2749            )?;
2750
2751            if let Some(ref interface) = shader_module.interface {
2752                let _ = interface.check_stage(
2753                    &mut binding_layout_source,
2754                    &mut shader_binding_sizes,
2755                    &final_entry_point_name,
2756                    stage,
2757                    io,
2758                    None,
2759                )?;
2760            }
2761        }
2762
2763        let pipeline_layout = match binding_layout_source {
2764            validation::BindingLayoutSource::Provided(_) => {
2765                drop(binding_layout_source);
2766                pipeline_layout.unwrap()
2767            }
2768            validation::BindingLayoutSource::Derived(entries) => {
2769                self.derive_pipeline_layout(entries)?
2770            }
2771        };
2772
2773        let late_sized_buffer_groups =
2774            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
2775
2776        let cache = match desc.cache {
2777            Some(cache) => {
2778                cache.same_device(self)?;
2779                Some(cache)
2780            }
2781            None => None,
2782        };
2783
2784        let pipeline_desc = hal::ComputePipelineDescriptor {
2785            label: desc.label.to_hal(self.instance_flags),
2786            layout: pipeline_layout.raw(),
2787            stage: hal::ProgrammableStage {
2788                module: shader_module.raw(),
2789                entry_point: final_entry_point_name.as_ref(),
2790                constants: desc.stage.constants.as_ref(),
2791                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
2792            },
2793            cache: cache.as_ref().map(|it| it.raw()),
2794        };
2795
2796        let raw =
2797            unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(
2798                |err| match err {
2799                    hal::PipelineError::Device(error) => {
2800                        pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))
2801                    }
2802                    hal::PipelineError::Linkage(_stages, msg) => {
2803                        pipeline::CreateComputePipelineError::Internal(msg)
2804                    }
2805                    hal::PipelineError::EntryPoint(_stage) => {
2806                        pipeline::CreateComputePipelineError::Internal(
2807                            ENTRYPOINT_FAILURE_ERROR.to_string(),
2808                        )
2809                    }
2810                    hal::PipelineError::PipelineConstants(_stages, msg) => {
2811                        pipeline::CreateComputePipelineError::PipelineConstants(msg)
2812                    }
2813                },
2814            )?;
2815
2816        let pipeline = pipeline::ComputePipeline {
2817            raw: ManuallyDrop::new(raw),
2818            layout: pipeline_layout,
2819            device: self.clone(),
2820            _shader_module: shader_module,
2821            late_sized_buffer_groups,
2822            label: desc.label.to_string(),
2823            tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),
2824        };
2825
2826        let pipeline = Arc::new(pipeline);
2827
2828        if is_auto_layout {
2829            for bgl in pipeline.layout.bind_group_layouts.iter() {
2830                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
2831                let _ = bgl
2832                    .exclusive_pipeline
2833                    .set(binding_model::ExclusivePipeline::Compute(Arc::downgrade(
2834                        &pipeline,
2835                    )));
2836            }
2837        }
2838
2839        Ok(pipeline)
2840    }
2841
2842    pub(crate) fn create_render_pipeline(
2843        self: &Arc<Self>,
2844        desc: pipeline::ResolvedRenderPipelineDescriptor,
2845    ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {
2846        use wgt::TextureFormatFeatureFlags as Tfff;
2847
2848        self.check_is_valid()?;
2849
2850        let mut shader_binding_sizes = FastHashMap::default();
2851
2852        let num_attachments = desc.fragment.as_ref().map(|f| f.targets.len()).unwrap_or(0);
2853        let max_attachments = self.limits.max_color_attachments as usize;
2854        if num_attachments > max_attachments {
2855            return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
2856                command::ColorAttachmentError::TooMany {
2857                    given: num_attachments,
2858                    limit: max_attachments,
2859                },
2860            ));
2861        }
2862
2863        let color_targets = desc
2864            .fragment
2865            .as_ref()
2866            .map_or(&[][..], |fragment| &fragment.targets);
2867        let depth_stencil_state = desc.depth_stencil.as_ref();
2868
2869        {
2870            let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
2871                color_targets.iter().filter_map(|x| x.as_ref()).collect();
2872            if !cts.is_empty() && {
2873                let first = &cts[0];
2874                cts[1..]
2875                    .iter()
2876                    .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
2877            } {
2878                self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
2879            }
2880        }
2881
2882        let mut io = validation::StageIo::default();
2883        let mut validated_stages = wgt::ShaderStages::empty();
2884
2885        let mut vertex_steps = Vec::with_capacity(desc.vertex.buffers.len());
2886        let mut vertex_buffers = Vec::with_capacity(desc.vertex.buffers.len());
2887        let mut total_attributes = 0;
2888        let mut shader_expects_dual_source_blending = false;
2889        let mut pipeline_expects_dual_source_blending = false;
2890        for (i, vb_state) in desc.vertex.buffers.iter().enumerate() {
2891            let mut last_stride = 0;
2892            for attribute in vb_state.attributes.iter() {
2893                last_stride = last_stride.max(attribute.offset + attribute.format.size());
2894            }
2895            vertex_steps.push(pipeline::VertexStep {
2896                stride: vb_state.array_stride,
2897                last_stride,
2898                mode: vb_state.step_mode,
2899            });
2900            if vb_state.attributes.is_empty() {
2901                continue;
2902            }
2903            if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
2904                return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
2905                    index: i as u32,
2906                    given: vb_state.array_stride as u32,
2907                    limit: self.limits.max_vertex_buffer_array_stride,
2908                });
2909            }
2910            if vb_state.array_stride % wgt::VERTEX_STRIDE_ALIGNMENT != 0 {
2911                return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
2912                    index: i as u32,
2913                    stride: vb_state.array_stride,
2914                });
2915            }
2916            vertex_buffers.push(hal::VertexBufferLayout {
2917                array_stride: vb_state.array_stride,
2918                step_mode: vb_state.step_mode,
2919                attributes: vb_state.attributes.as_ref(),
2920            });
2921
2922            for attribute in vb_state.attributes.iter() {
2923                if attribute.offset >= 0x10000000 {
2924                    return Err(
2925                        pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
2926                            location: attribute.shader_location,
2927                            offset: attribute.offset,
2928                        },
2929                    );
2930                }
2931
2932                if let wgt::VertexFormat::Float64
2933                | wgt::VertexFormat::Float64x2
2934                | wgt::VertexFormat::Float64x3
2935                | wgt::VertexFormat::Float64x4 = attribute.format
2936                {
2937                    self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
2938                }
2939
2940                let previous = io.insert(
2941                    attribute.shader_location,
2942                    validation::InterfaceVar::vertex_attribute(attribute.format),
2943                );
2944
2945                if previous.is_some() {
2946                    return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
2947                        attribute.shader_location,
2948                    ));
2949                }
2950            }
2951            total_attributes += vb_state.attributes.len();
2952        }
2953
2954        if vertex_buffers.len() > self.limits.max_vertex_buffers as usize {
2955            return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
2956                given: vertex_buffers.len() as u32,
2957                limit: self.limits.max_vertex_buffers,
2958            });
2959        }
2960        if total_attributes > self.limits.max_vertex_attributes as usize {
2961            return Err(
2962                pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
2963                    given: total_attributes as u32,
2964                    limit: self.limits.max_vertex_attributes,
2965                },
2966            );
2967        }
2968
2969        if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
2970            return Err(
2971                pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
2972                    strip_index_format: desc.primitive.strip_index_format,
2973                    topology: desc.primitive.topology,
2974                },
2975            );
2976        }
2977
2978        if desc.primitive.unclipped_depth {
2979            self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
2980        }
2981
2982        if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
2983            self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
2984        }
2985        if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
2986            self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
2987        }
2988
2989        if desc.primitive.conservative {
2990            self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
2991        }
2992
2993        if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
2994            return Err(
2995                pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
2996            );
2997        }
2998
2999        let mut target_specified = false;
3000
3001        for (i, cs) in color_targets.iter().enumerate() {
3002            if let Some(cs) = cs.as_ref() {
3003                target_specified = true;
3004                let error = 'error: {
3005                    if cs.write_mask.contains_invalid_bits() {
3006                        break 'error Some(pipeline::ColorStateError::InvalidWriteMask(
3007                            cs.write_mask,
3008                        ));
3009                    }
3010
3011                    let format_features = self.describe_format_features(cs.format)?;
3012                    if !format_features
3013                        .allowed_usages
3014                        .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
3015                    {
3016                        break 'error Some(pipeline::ColorStateError::FormatNotRenderable(
3017                            cs.format,
3018                        ));
3019                    }
3020                    let blendable = format_features.flags.contains(Tfff::BLENDABLE);
3021                    let filterable = format_features.flags.contains(Tfff::FILTERABLE);
3022                    let adapter_specific = self
3023                        .features
3024                        .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
3025                    // according to WebGPU specifications the texture needs to be
3026                    // [`TextureFormatFeatureFlags::FILTERABLE`] if blending is set - use
3027                    // [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] to elude
3028                    // this limitation
3029                    if cs.blend.is_some() && (!blendable || (!filterable && !adapter_specific)) {
3030                        break 'error Some(pipeline::ColorStateError::FormatNotBlendable(
3031                            cs.format,
3032                        ));
3033                    }
3034                    if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
3035                        break 'error Some(pipeline::ColorStateError::FormatNotColor(cs.format));
3036                    }
3037
3038                    if desc.multisample.count > 1
3039                        && !format_features
3040                            .flags
3041                            .sample_count_supported(desc.multisample.count)
3042                    {
3043                        break 'error Some(pipeline::ColorStateError::InvalidSampleCount(
3044                            desc.multisample.count,
3045                            cs.format,
3046                            cs.format
3047                                .guaranteed_format_features(self.features)
3048                                .flags
3049                                .supported_sample_counts(),
3050                            self.adapter
3051                                .get_texture_format_features(cs.format)
3052                                .flags
3053                                .supported_sample_counts(),
3054                        ));
3055                    }
3056
3057                    if let Some(blend_mode) = cs.blend {
3058                        for factor in [
3059                            blend_mode.color.src_factor,
3060                            blend_mode.color.dst_factor,
3061                            blend_mode.alpha.src_factor,
3062                            blend_mode.alpha.dst_factor,
3063                        ] {
3064                            if factor.ref_second_blend_source() {
3065                                self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
3066                                if i == 0 {
3067                                    pipeline_expects_dual_source_blending = true;
3068                                    break;
3069                                } else {
3070                                    return Err(pipeline::CreateRenderPipelineError
3071                                        ::BlendFactorOnUnsupportedTarget { factor, target: i as u32 });
3072                                }
3073                            }
3074                        }
3075                    }
3076
3077                    break 'error None;
3078                };
3079                if let Some(e) = error {
3080                    return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
3081                }
3082            }
3083        }
3084
3085        let limit = self.limits.max_color_attachment_bytes_per_sample;
3086        let formats = color_targets
3087            .iter()
3088            .map(|cs| cs.as_ref().map(|cs| cs.format));
3089        if let Err(total) = validate_color_attachment_bytes_per_sample(formats, limit) {
3090            return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
3091                command::ColorAttachmentError::TooManyBytesPerSample { total, limit },
3092            ));
3093        }
3094
3095        if let Some(ds) = depth_stencil_state {
3096            target_specified = true;
3097            let error = 'error: {
3098                let format_features = self.describe_format_features(ds.format)?;
3099                if !format_features
3100                    .allowed_usages
3101                    .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
3102                {
3103                    break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(
3104                        ds.format,
3105                    ));
3106                }
3107
3108                let aspect = hal::FormatAspects::from(ds.format);
3109                if ds.is_depth_enabled() && !aspect.contains(hal::FormatAspects::DEPTH) {
3110                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
3111                }
3112                if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
3113                    break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(
3114                        ds.format,
3115                    ));
3116                }
3117                if desc.multisample.count > 1
3118                    && !format_features
3119                        .flags
3120                        .sample_count_supported(desc.multisample.count)
3121                {
3122                    break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(
3123                        desc.multisample.count,
3124                        ds.format,
3125                        ds.format
3126                            .guaranteed_format_features(self.features)
3127                            .flags
3128                            .supported_sample_counts(),
3129                        self.adapter
3130                            .get_texture_format_features(ds.format)
3131                            .flags
3132                            .supported_sample_counts(),
3133                    ));
3134                }
3135
3136                break 'error None;
3137            };
3138            if let Some(e) = error {
3139                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
3140            }
3141
3142            if ds.bias.clamp != 0.0 {
3143                self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
3144            }
3145        }
3146
3147        if !target_specified {
3148            return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);
3149        }
3150
3151        let is_auto_layout = desc.layout.is_none();
3152
3153        // Get the pipeline layout from the desc if it is provided.
3154        let pipeline_layout = match desc.layout {
3155            Some(pipeline_layout) => {
3156                pipeline_layout.same_device(self)?;
3157                Some(pipeline_layout)
3158            }
3159            None => None,
3160        };
3161
3162        let mut binding_layout_source = match pipeline_layout {
3163            Some(ref pipeline_layout) => {
3164                validation::BindingLayoutSource::Provided(pipeline_layout.get_binding_maps())
3165            }
3166            None => validation::BindingLayoutSource::new_derived(&self.limits),
3167        };
3168
3169        let samples = {
3170            let sc = desc.multisample.count;
3171            if sc == 0 || sc > 32 || !sc.is_power_of_two() {
3172                return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
3173            }
3174            sc
3175        };
3176
3177        let vertex_entry_point_name;
3178        let vertex_stage = {
3179            let stage_desc = &desc.vertex.stage;
3180            let stage = wgt::ShaderStages::VERTEX;
3181
3182            let vertex_shader_module = &stage_desc.module;
3183            vertex_shader_module.same_device(self)?;
3184
3185            let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
3186
3187            vertex_entry_point_name = vertex_shader_module
3188                .finalize_entry_point_name(
3189                    stage,
3190                    stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
3191                )
3192                .map_err(stage_err)?;
3193
3194            if let Some(ref interface) = vertex_shader_module.interface {
3195                io = interface
3196                    .check_stage(
3197                        &mut binding_layout_source,
3198                        &mut shader_binding_sizes,
3199                        &vertex_entry_point_name,
3200                        stage,
3201                        io,
3202                        desc.depth_stencil.as_ref().map(|d| d.depth_compare),
3203                    )
3204                    .map_err(stage_err)?;
3205                validated_stages |= stage;
3206            }
3207
3208            hal::ProgrammableStage {
3209                module: vertex_shader_module.raw(),
3210                entry_point: &vertex_entry_point_name,
3211                constants: stage_desc.constants.as_ref(),
3212                zero_initialize_workgroup_memory: stage_desc.zero_initialize_workgroup_memory,
3213            }
3214        };
3215
3216        let fragment_entry_point_name;
3217        let fragment_stage = match desc.fragment {
3218            Some(ref fragment_state) => {
3219                let stage = wgt::ShaderStages::FRAGMENT;
3220
3221                let shader_module = &fragment_state.stage.module;
3222                shader_module.same_device(self)?;
3223
3224                let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
3225
3226                fragment_entry_point_name = shader_module
3227                    .finalize_entry_point_name(
3228                        stage,
3229                        fragment_state
3230                            .stage
3231                            .entry_point
3232                            .as_ref()
3233                            .map(|ep| ep.as_ref()),
3234                    )
3235                    .map_err(stage_err)?;
3236
3237                if validated_stages == wgt::ShaderStages::VERTEX {
3238                    if let Some(ref interface) = shader_module.interface {
3239                        io = interface
3240                            .check_stage(
3241                                &mut binding_layout_source,
3242                                &mut shader_binding_sizes,
3243                                &fragment_entry_point_name,
3244                                stage,
3245                                io,
3246                                desc.depth_stencil.as_ref().map(|d| d.depth_compare),
3247                            )
3248                            .map_err(stage_err)?;
3249                        validated_stages |= stage;
3250                    }
3251                }
3252
3253                if let Some(ref interface) = shader_module.interface {
3254                    shader_expects_dual_source_blending = interface
3255                        .fragment_uses_dual_source_blending(&fragment_entry_point_name)
3256                        .map_err(|error| pipeline::CreateRenderPipelineError::Stage {
3257                            stage,
3258                            error,
3259                        })?;
3260                }
3261
3262                Some(hal::ProgrammableStage {
3263                    module: shader_module.raw(),
3264                    entry_point: &fragment_entry_point_name,
3265                    constants: fragment_state.stage.constants.as_ref(),
3266                    zero_initialize_workgroup_memory: fragment_state
3267                        .stage
3268                        .zero_initialize_workgroup_memory,
3269                })
3270            }
3271            None => None,
3272        };
3273
3274        if !pipeline_expects_dual_source_blending && shader_expects_dual_source_blending {
3275            return Err(
3276                pipeline::CreateRenderPipelineError::ShaderExpectsPipelineToUseDualSourceBlending,
3277            );
3278        }
3279        if pipeline_expects_dual_source_blending && !shader_expects_dual_source_blending {
3280            return Err(
3281                pipeline::CreateRenderPipelineError::PipelineExpectsShaderToUseDualSourceBlending,
3282            );
3283        }
3284
3285        if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
3286            for (i, output) in io.iter() {
3287                match color_targets.get(*i as usize) {
3288                    Some(Some(state)) => {
3289                        validation::check_texture_format(state.format, &output.ty).map_err(
3290                            |pipeline| {
3291                                pipeline::CreateRenderPipelineError::ColorState(
3292                                    *i as u8,
3293                                    pipeline::ColorStateError::IncompatibleFormat {
3294                                        pipeline,
3295                                        shader: output.ty,
3296                                    },
3297                                )
3298                            },
3299                        )?;
3300                    }
3301                    _ => {
3302                        log::warn!(
3303                            "The fragment stage {:?} output @location({}) values are ignored",
3304                            fragment_stage
3305                                .as_ref()
3306                                .map_or("", |stage| stage.entry_point),
3307                            i
3308                        );
3309                    }
3310                }
3311            }
3312        }
3313        let last_stage = match desc.fragment {
3314            Some(_) => wgt::ShaderStages::FRAGMENT,
3315            None => wgt::ShaderStages::VERTEX,
3316        };
3317        if is_auto_layout && !validated_stages.contains(last_stage) {
3318            return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
3319        }
3320
3321        let pipeline_layout = match binding_layout_source {
3322            validation::BindingLayoutSource::Provided(_) => {
3323                drop(binding_layout_source);
3324                pipeline_layout.unwrap()
3325            }
3326            validation::BindingLayoutSource::Derived(entries) => {
3327                self.derive_pipeline_layout(entries)?
3328            }
3329        };
3330
3331        // Multiview is only supported if the feature is enabled
3332        if desc.multiview.is_some() {
3333            self.require_features(wgt::Features::MULTIVIEW)?;
3334        }
3335
3336        if !self
3337            .downlevel
3338            .flags
3339            .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
3340        {
3341            for (binding, size) in shader_binding_sizes.iter() {
3342                if size.get() % 16 != 0 {
3343                    return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
3344                        binding: binding.binding,
3345                        group: binding.group,
3346                        size: size.get(),
3347                    });
3348                }
3349            }
3350        }
3351
3352        let late_sized_buffer_groups =
3353            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
3354
3355        let cache = match desc.cache {
3356            Some(cache) => {
3357                cache.same_device(self)?;
3358                Some(cache)
3359            }
3360            None => None,
3361        };
3362
3363        let pipeline_desc = hal::RenderPipelineDescriptor {
3364            label: desc.label.to_hal(self.instance_flags),
3365            layout: pipeline_layout.raw(),
3366            vertex_buffers: &vertex_buffers,
3367            vertex_stage,
3368            primitive: desc.primitive,
3369            depth_stencil: desc.depth_stencil.clone(),
3370            multisample: desc.multisample,
3371            fragment_stage,
3372            color_targets,
3373            multiview: desc.multiview,
3374            cache: cache.as_ref().map(|it| it.raw()),
3375        };
3376        let raw =
3377            unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
3378                |err| match err {
3379                    hal::PipelineError::Device(error) => {
3380                        pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))
3381                    }
3382                    hal::PipelineError::Linkage(stage, msg) => {
3383                        pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
3384                    }
3385                    hal::PipelineError::EntryPoint(stage) => {
3386                        pipeline::CreateRenderPipelineError::Internal {
3387                            stage: hal::auxil::map_naga_stage(stage),
3388                            error: ENTRYPOINT_FAILURE_ERROR.to_string(),
3389                        }
3390                    }
3391                    hal::PipelineError::PipelineConstants(stage, error) => {
3392                        pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }
3393                    }
3394                },
3395            )?;
3396
3397        let pass_context = RenderPassContext {
3398            attachments: AttachmentData {
3399                colors: color_targets
3400                    .iter()
3401                    .map(|state| state.as_ref().map(|s| s.format))
3402                    .collect(),
3403                resolves: ArrayVec::new(),
3404                depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
3405            },
3406            sample_count: samples,
3407            multiview: desc.multiview,
3408        };
3409
3410        let mut flags = pipeline::PipelineFlags::empty();
3411        for state in color_targets.iter().filter_map(|s| s.as_ref()) {
3412            if let Some(ref bs) = state.blend {
3413                if bs.color.uses_constant() | bs.alpha.uses_constant() {
3414                    flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
3415                }
3416            }
3417        }
3418        if let Some(ds) = depth_stencil_state.as_ref() {
3419            if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
3420                flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
3421            }
3422            if !ds.is_depth_read_only() {
3423                flags |= pipeline::PipelineFlags::WRITES_DEPTH;
3424            }
3425            if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
3426                flags |= pipeline::PipelineFlags::WRITES_STENCIL;
3427            }
3428        }
3429
3430        let shader_modules = {
3431            let mut shader_modules = ArrayVec::new();
3432            shader_modules.push(desc.vertex.stage.module);
3433            shader_modules.extend(desc.fragment.map(|f| f.stage.module));
3434            shader_modules
3435        };
3436
3437        let pipeline = pipeline::RenderPipeline {
3438            raw: ManuallyDrop::new(raw),
3439            layout: pipeline_layout,
3440            device: self.clone(),
3441            pass_context,
3442            _shader_modules: shader_modules,
3443            flags,
3444            strip_index_format: desc.primitive.strip_index_format,
3445            vertex_steps,
3446            late_sized_buffer_groups,
3447            label: desc.label.to_string(),
3448            tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),
3449        };
3450
3451        let pipeline = Arc::new(pipeline);
3452
3453        if is_auto_layout {
3454            for bgl in pipeline.layout.bind_group_layouts.iter() {
3455                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
3456                let _ = bgl
3457                    .exclusive_pipeline
3458                    .set(binding_model::ExclusivePipeline::Render(Arc::downgrade(
3459                        &pipeline,
3460                    )));
3461            }
3462        }
3463
3464        Ok(pipeline)
3465    }
3466
3467    /// # Safety
3468    /// The `data` field on `desc` must have previously been returned from [`crate::global::Global::pipeline_cache_get_data`]
3469    pub unsafe fn create_pipeline_cache(
3470        self: &Arc<Self>,
3471        desc: &pipeline::PipelineCacheDescriptor,
3472    ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {
3473        use crate::pipeline_cache;
3474
3475        self.check_is_valid()?;
3476
3477        self.require_features(wgt::Features::PIPELINE_CACHE)?;
3478        let data = if let Some((data, validation_key)) = desc
3479            .data
3480            .as_ref()
3481            .zip(self.raw().pipeline_cache_validation_key())
3482        {
3483            let data = pipeline_cache::validate_pipeline_cache(
3484                data,
3485                &self.adapter.raw.info,
3486                validation_key,
3487            );
3488            match data {
3489                Ok(data) => Some(data),
3490                Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),
3491                // If the error was unavoidable and we are asked to fallback, do so
3492                Err(_) => None,
3493            }
3494        } else {
3495            None
3496        };
3497        let cache_desc = hal::PipelineCacheDescriptor {
3498            data,
3499            label: desc.label.to_hal(self.instance_flags),
3500        };
3501        let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {
3502            Ok(raw) => raw,
3503            Err(e) => match e {
3504                hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),
3505            },
3506        };
3507        let cache = pipeline::PipelineCache {
3508            device: self.clone(),
3509            label: desc.label.to_string(),
3510            // This would be none in the error condition, which we don't implement yet
3511            raw: ManuallyDrop::new(raw),
3512        };
3513
3514        let cache = Arc::new(cache);
3515
3516        Ok(cache)
3517    }
3518
3519    fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {
3520        // Variant of adapter.get_texture_format_features that takes device features into account
3521        use wgt::TextureFormatFeatureFlags as tfsc;
3522        let mut format_features = self.adapter.get_texture_format_features(format);
3523        if (format == TextureFormat::R32Float
3524            || format == TextureFormat::Rg32Float
3525            || format == TextureFormat::Rgba32Float)
3526            && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
3527        {
3528            format_features.flags.set(tfsc::FILTERABLE, false);
3529        }
3530        format_features
3531    }
3532
3533    fn describe_format_features(
3534        &self,
3535        format: TextureFormat,
3536    ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
3537        self.require_features(format.required_features())?;
3538
3539        let using_device_features = self
3540            .features
3541            .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
3542        // If we're running downlevel, we need to manually ask the backend what
3543        // we can use as we can't trust WebGPU.
3544        let downlevel = !self
3545            .downlevel
3546            .flags
3547            .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
3548
3549        if using_device_features || downlevel {
3550            Ok(self.get_texture_format_features(format))
3551        } else {
3552            Ok(format.guaranteed_format_features(self.features))
3553        }
3554    }
3555
3556    #[cfg(feature = "replay")]
3557    pub(crate) fn wait_for_submit(
3558        &self,
3559        submission_index: crate::SubmissionIndex,
3560    ) -> Result<(), DeviceError> {
3561        let fence = self.fence.read();
3562        let last_done_index = unsafe { self.raw().get_fence_value(fence.as_ref()) }
3563            .map_err(|e| self.handle_hal_error(e))?;
3564        if last_done_index < submission_index {
3565            unsafe { self.raw().wait(fence.as_ref(), submission_index, !0) }
3566                .map_err(|e| self.handle_hal_error(e))?;
3567            drop(fence);
3568            let closures = self
3569                .lock_life()
3570                .triage_submissions(submission_index, &self.command_allocator);
3571            assert!(
3572                closures.is_empty(),
3573                "wait_for_submit is not expected to work with closures"
3574            );
3575        }
3576        Ok(())
3577    }
3578
3579    pub(crate) fn create_query_set(
3580        self: &Arc<Self>,
3581        desc: &resource::QuerySetDescriptor,
3582    ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {
3583        use resource::CreateQuerySetError as Error;
3584
3585        self.check_is_valid()?;
3586
3587        match desc.ty {
3588            wgt::QueryType::Occlusion => {}
3589            wgt::QueryType::Timestamp => {
3590                self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
3591            }
3592            wgt::QueryType::PipelineStatistics(..) => {
3593                self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
3594            }
3595        }
3596
3597        if desc.count == 0 {
3598            return Err(Error::ZeroCount);
3599        }
3600
3601        if desc.count > wgt::QUERY_SET_MAX_QUERIES {
3602            return Err(Error::TooManyQueries {
3603                count: desc.count,
3604                maximum: wgt::QUERY_SET_MAX_QUERIES,
3605            });
3606        }
3607
3608        let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
3609
3610        let raw = unsafe { self.raw().create_query_set(&hal_desc).unwrap() };
3611
3612        let query_set = QuerySet {
3613            raw: ManuallyDrop::new(raw),
3614            device: self.clone(),
3615            label: desc.label.to_string(),
3616            tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),
3617            desc: desc.map_label(|_| ()),
3618        };
3619
3620        let query_set = Arc::new(query_set);
3621
3622        Ok(query_set)
3623    }
3624
3625    fn lose(&self, message: &str) {
3626        // Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device.
3627
3628        // Mark the device explicitly as invalid. This is checked in various
3629        // places to prevent new work from being submitted.
3630        self.valid.store(false, Ordering::Release);
3631
3632        // 1) Resolve the GPUDevice device.lost promise.
3633        let mut life_lock = self.lock_life();
3634        let closure = life_lock.device_lost_closure.take();
3635        // It's important to not hold the lock while calling the closure and while calling
3636        // release_gpu_resources which may take the lock again.
3637        drop(life_lock);
3638
3639        if let Some(device_lost_closure) = closure {
3640            device_lost_closure.call(DeviceLostReason::Unknown, message.to_string());
3641        }
3642
3643        // 2) Complete any outstanding mapAsync() steps.
3644        // 3) Complete any outstanding onSubmittedWorkDone() steps.
3645
3646        // These parts are passively accomplished by setting valid to false,
3647        // since that will prevent any new work from being added to the queues.
3648        // Future calls to poll_devices will continue to check the work queues
3649        // until they are cleared, and then drop the device.
3650
3651        // Eagerly release GPU resources.
3652        self.release_gpu_resources();
3653    }
3654
3655    pub(crate) fn release_gpu_resources(&self) {
3656        // This is called when the device is lost, which makes every associated
3657        // resource invalid and unusable. This is an opportunity to release all of
3658        // the underlying gpu resources, even though the objects remain visible to
3659        // the user agent. We purge this memory naturally when resources have been
3660        // moved into the appropriate buckets, so this function just needs to
3661        // initiate movement into those buckets, and it can do that by calling
3662        // "destroy" on all the resources we know about.
3663
3664        // During these iterations, we discard all errors. We don't care!
3665        let trackers = self.trackers.lock();
3666        for buffer in trackers.buffers.used_resources() {
3667            if let Some(buffer) = Weak::upgrade(&buffer) {
3668                let _ = buffer.destroy();
3669            }
3670        }
3671        for texture in trackers.textures.used_resources() {
3672            if let Some(texture) = Weak::upgrade(&texture) {
3673                let _ = texture.destroy();
3674            }
3675        }
3676    }
3677
3678    pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {
3679        UsageScope::new_pooled(&self.usage_scopes, &self.tracker_indices)
3680    }
3681
3682    pub fn get_hal_counters(&self) -> wgt::HalCounters {
3683        self.raw().get_internal_counters()
3684    }
3685
3686    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
3687        self.raw().generate_allocator_report()
3688    }
3689}
3690
3691impl Device {
3692    /// Wait for idle and remove resources that we can, before we die.
3693    pub(crate) fn prepare_to_die(&self) {
3694        self.pending_writes.lock().deactivate();
3695        let current_index = self
3696            .last_successful_submission_index
3697            .load(Ordering::Acquire);
3698        if let Err(error) = unsafe {
3699            let fence = self.fence.read();
3700            self.raw()
3701                .wait(fence.as_ref(), current_index, CLEANUP_WAIT_MS)
3702        } {
3703            log::error!("failed to wait for the device: {error}");
3704        }
3705        let mut life_tracker = self.lock_life();
3706        let _ = life_tracker.triage_submissions(current_index, &self.command_allocator);
3707        if let Some(device_lost_closure) = life_tracker.device_lost_closure.take() {
3708            // It's important to not hold the lock while calling the closure.
3709            drop(life_tracker);
3710            device_lost_closure.call(DeviceLostReason::Dropped, "Device is dying.".to_string());
3711        }
3712        #[cfg(feature = "trace")]
3713        {
3714            *self.trace.lock() = None;
3715        }
3716    }
3717}
3718
3719crate::impl_resource_type!(Device);
3720crate::impl_labeled!(Device);
3721crate::impl_storage_item!(Device);