wgpu_core/device/
resource.rs

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