wgpu_core/device/
resource.rs

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