li_wgpu_core/device/
resource.rs

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