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