wgpu_core/device/
resource.rs

1use alloc::{
2    borrow::Cow,
3    boxed::Box,
4    string::{String, ToString as _},
5    sync::{Arc, Weak},
6    vec::Vec,
7};
8use core::{
9    fmt,
10    mem::{self, ManuallyDrop},
11    num::NonZeroU32,
12    sync::atomic::{AtomicBool, Ordering},
13};
14use hal::ShouldBeNonZeroExt;
15
16use arrayvec::ArrayVec;
17use bitflags::Flags;
18use smallvec::SmallVec;
19use wgt::{
20    math::align_to, DeviceLostReason, TextureFormat, TextureSampleType, TextureSelector,
21    TextureViewDimension,
22};
23
24#[cfg(feature = "trace")]
25use crate::device::trace;
26use crate::{
27    api_log,
28    binding_model::{
29        self, BindGroup, BindGroupLateBufferBindingInfo, BindGroupLayout, BindGroupLayoutEntryError,
30    },
31    command, conv,
32    device::{
33        bgl, create_validator, life::WaitIdleError, map_buffer, AttachmentData,
34        DeviceLostInvocation, HostMap, MissingDownlevelFlags, MissingFeatures, RenderPassContext,
35    },
36    hal_label,
37    init_tracker::{
38        BufferInitTracker, BufferInitTrackerAction, MemoryInitKind, TextureInitRange,
39        TextureInitTrackerAction,
40    },
41    instance::{Adapter, RequestDeviceError},
42    lock::{rank, Mutex, RwLock},
43    pipeline,
44    pool::ResourcePool,
45    present,
46    resource::{
47        self, Buffer, ExternalTexture, Fallible, Labeled, ParentDevice, QuerySet,
48        RawResourceAccess, Sampler, StagingBuffer, Texture, TextureView,
49        TextureViewNotRenderableReason, Tlas, TrackingData,
50    },
51    resource_log,
52    snatch::{SnatchGuard, SnatchLock, Snatchable},
53    timestamp_normalization::TIMESTAMP_NORMALIZATION_BUFFER_USES,
54    track::{BindGroupStates, DeviceTracker, TrackerIndexAllocators, UsageScope, UsageScopePool},
55    validation::{self, validate_color_attachment_bytes_per_sample},
56    weak_vec::WeakVec,
57    FastHashMap, LabelHelpers, OnceCellOrLock,
58};
59
60use super::{
61    queue::Queue, DeviceDescriptor, DeviceError, DeviceLostClosure, UserClosures,
62    ENTRYPOINT_FAILURE_ERROR, ZERO_BUFFER_SIZE,
63};
64
65#[cfg(supports_64bit_atomics)]
66use core::sync::atomic::AtomicU64;
67#[cfg(not(supports_64bit_atomics))]
68use portable_atomic::AtomicU64;
69
70pub(crate) struct CommandIndices {
71    /// The index of the last command submission that was attempted.
72    ///
73    /// Note that `fence` may never be signalled with this value, if the command
74    /// submission failed. If you need to wait for everything running on a
75    /// `Queue` to complete, wait for [`last_successful_submission_index`].
76    ///
77    /// [`last_successful_submission_index`]: Device::last_successful_submission_index
78    pub(crate) active_submission_index: hal::FenceValue,
79    pub(crate) next_acceleration_structure_build_command_index: u64,
80}
81
82/// Parameters provided to shaders via a uniform buffer of the type
83/// [`NagaExternalTextureParams`], describing an [`ExternalTexture`] resource
84/// binding.
85///
86/// [`NagaExternalTextureParams`]: naga::SpecialTypes::external_texture_params
87/// [`ExternalTexture`]: binding_model::BindingResource::ExternalTexture
88#[repr(C)]
89#[derive(Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]
90pub struct ExternalTextureParams {
91    /// 4x4 column-major matrix with which to convert sampled YCbCr values
92    /// to RGBA.
93    ///
94    /// This is ignored when `num_planes` is 1.
95    pub yuv_conversion_matrix: [f32; 16],
96
97    /// 3x3 column-major matrix to transform linear RGB values in the source
98    /// color space to linear RGB values in the destination color space. In
99    /// combination with [`Self::src_transfer_function`] and
100    /// [`Self::dst_transfer_function`] this can be used to ensure that
101    /// [`ImageSample`] and [`ImageLoad`] operations return values in the
102    /// desired destination color space rather than the source color space of
103    /// the underlying planes.
104    ///
105    /// Includes a padding element after each column.
106    ///
107    /// [`ImageSample`]: naga::ir::Expression::ImageSample
108    /// [`ImageLoad`]: naga::ir::Expression::ImageLoad
109    pub gamut_conversion_matrix: [f32; 12],
110
111    /// Transfer function for the source color space. The *inverse* of this
112    /// will be applied to decode non-linear RGB to linear RGB in the source
113    /// color space.
114    pub src_transfer_function: wgt::ExternalTextureTransferFunction,
115
116    /// Transfer function for the destination color space. This will be applied
117    /// to encode linear RGB to non-linear RGB in the destination color space.
118    pub dst_transfer_function: wgt::ExternalTextureTransferFunction,
119
120    /// Transform to apply to [`ImageSample`] coordinates.
121    ///
122    /// This is a 3x2 column-major matrix representing an affine transform from
123    /// normalized texture coordinates to the normalized coordinates that should
124    /// be sampled from the external texture's underlying plane(s).
125    ///
126    /// This transform may scale, translate, flip, and rotate in 90-degree
127    /// increments, but the result of transforming the rectangle (0,0)..(1,1)
128    /// must be an axis-aligned rectangle that falls within the bounds of
129    /// (0,0)..(1,1).
130    ///
131    /// [`ImageSample`]: naga::ir::Expression::ImageSample
132    pub sample_transform: [f32; 6],
133
134    /// Transform to apply to [`ImageLoad`] coordinates.
135    ///
136    /// This is a 3x2 column-major matrix representing an affine transform from
137    /// non-normalized texel coordinates to the non-normalized coordinates of
138    /// the texel that should be loaded from the external texture's underlying
139    /// plane 0. For planes 1 and 2, if present, plane 0's coordinates are
140    /// scaled according to the textures' relative sizes.
141    ///
142    /// This transform may scale, translate, flip, and rotate in 90-degree
143    /// increments, but the result of transforming the rectangle (0,0)..[`size`]
144    /// must be an axis-aligned rectangle that falls within the bounds of
145    /// (0,0)..[`size`].
146    ///
147    /// [`ImageLoad`]: naga::ir::Expression::ImageLoad
148    /// [`size`]: Self::size
149    pub load_transform: [f32; 6],
150
151    /// Size of the external texture.
152    ///
153    /// This is the value that should be returned by size queries in shader
154    /// code; it does not necessarily match the dimensions of the underlying
155    /// texture(s). As a special case, if this is `[0, 0]`, the actual size of
156    /// plane 0 should be used instead.
157    ///
158    /// This must be consistent with [`sample_transform`]: it should be the size
159    /// in texels of the rectangle covered by the square (0,0)..(1,1) after
160    /// [`sample_transform`] has been applied to it.
161    ///
162    /// [`sample_transform`]: Self::sample_transform
163    pub size: [u32; 2],
164
165    /// Number of planes. 1 indicates a single RGBA plane. 2 indicates a Y
166    /// plane and an interleaved CbCr plane. 3 indicates separate Y, Cb, and Cr
167    /// planes.
168    pub num_planes: u32,
169    // Ensure the size of this struct matches the type generated by Naga.
170    pub _padding: [u8; 4],
171}
172
173impl ExternalTextureParams {
174    pub fn from_desc<L>(desc: &wgt::ExternalTextureDescriptor<L>) -> Self {
175        let gamut_conversion_matrix = [
176            desc.gamut_conversion_matrix[0],
177            desc.gamut_conversion_matrix[1],
178            desc.gamut_conversion_matrix[2],
179            0.0, // padding
180            desc.gamut_conversion_matrix[3],
181            desc.gamut_conversion_matrix[4],
182            desc.gamut_conversion_matrix[5],
183            0.0, // padding
184            desc.gamut_conversion_matrix[6],
185            desc.gamut_conversion_matrix[7],
186            desc.gamut_conversion_matrix[8],
187            0.0, // padding
188        ];
189
190        Self {
191            yuv_conversion_matrix: desc.yuv_conversion_matrix,
192            gamut_conversion_matrix,
193            src_transfer_function: desc.src_transfer_function,
194            dst_transfer_function: desc.dst_transfer_function,
195            size: [desc.width, desc.height],
196            sample_transform: desc.sample_transform,
197            load_transform: desc.load_transform,
198            num_planes: desc.num_planes() as u32,
199            _padding: Default::default(),
200        }
201    }
202}
203
204/// Structure describing a logical device. Some members are internally mutable,
205/// stored behind mutexes.
206pub struct Device {
207    raw: Box<dyn hal::DynDevice>,
208    pub(crate) adapter: Arc<Adapter>,
209    pub(crate) queue: OnceCellOrLock<Weak<Queue>>,
210    pub(crate) zero_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
211    /// The `label` from the descriptor used to create the resource.
212    label: String,
213
214    pub(crate) command_allocator: command::CommandAllocator,
215
216    pub(crate) command_indices: RwLock<CommandIndices>,
217
218    /// The index of the last successful submission to this device's
219    /// [`hal::Queue`].
220    ///
221    /// Unlike [`active_submission_index`], which is incremented each time
222    /// submission is attempted, this is updated only when submission succeeds,
223    /// so waiting for this value won't hang waiting for work that was never
224    /// submitted.
225    ///
226    /// [`active_submission_index`]: CommandIndices::active_submission_index
227    pub(crate) last_successful_submission_index: hal::AtomicFenceValue,
228
229    // NOTE: if both are needed, the `snatchable_lock` must be consistently acquired before the
230    // `fence` lock to avoid deadlocks.
231    pub(crate) fence: RwLock<ManuallyDrop<Box<dyn hal::DynFence>>>,
232    pub(crate) snatchable_lock: SnatchLock,
233
234    /// Is this device valid? Valid is closely associated with "lose the device",
235    /// which can be triggered by various methods, including at the end of device
236    /// destroy, and by any GPU errors that cause us to no longer trust the state
237    /// of the device. Ideally we would like to fold valid into the storage of
238    /// the device itself (for example as an Error enum), but unfortunately we
239    /// need to continue to be able to retrieve the device in poll_devices to
240    /// determine if it can be dropped. If our internal accesses of devices were
241    /// done through ref-counted references and external accesses checked for
242    /// Error enums, we wouldn't need this. For now, we need it. All the call
243    /// sites where we check it are areas that should be revisited if we start
244    /// using ref-counted references for internal access.
245    pub(crate) valid: AtomicBool,
246
247    /// Closure to be called on "lose the device". This is invoked directly by
248    /// device.lose or by the UserCallbacks returned from maintain when the device
249    /// has been destroyed and its queues are empty.
250    pub(crate) device_lost_closure: Mutex<Option<DeviceLostClosure>>,
251
252    /// Stores the state of buffers and textures.
253    pub(crate) trackers: Mutex<DeviceTracker>,
254    pub(crate) tracker_indices: TrackerIndexAllocators,
255    /// Pool of bind group layouts, allowing deduplication.
256    pub(crate) bgl_pool: ResourcePool<bgl::EntryMap, BindGroupLayout>,
257    pub(crate) alignments: hal::Alignments,
258    pub(crate) limits: wgt::Limits,
259    pub(crate) features: wgt::Features,
260    pub(crate) downlevel: wgt::DownlevelCapabilities,
261    pub(crate) instance_flags: wgt::InstanceFlags,
262    pub(crate) deferred_destroy: Mutex<Vec<DeferredDestroy>>,
263    pub(crate) usage_scopes: UsageScopePool,
264    pub(crate) indirect_validation: Option<crate::indirect_validation::IndirectValidation>,
265    // Optional so that we can late-initialize this after the queue is created.
266    pub(crate) timestamp_normalizer:
267        OnceCellOrLock<crate::timestamp_normalization::TimestampNormalizer>,
268    /// Uniform buffer containing [`ExternalTextureParams`] with values such
269    /// that a [`TextureView`] bound to a [`wgt::BindingType::ExternalTexture`]
270    /// binding point will be rendered correctly. Intended to be used as the
271    /// [`hal::ExternalTextureBinding::params`] field.
272    pub(crate) default_external_texture_params_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
273    // needs to be dropped last
274    #[cfg(feature = "trace")]
275    pub(crate) trace: Mutex<Option<trace::Trace>>,
276}
277
278pub(crate) enum DeferredDestroy {
279    TextureViews(WeakVec<TextureView>),
280    BindGroups(WeakVec<BindGroup>),
281}
282
283impl fmt::Debug for Device {
284    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285        f.debug_struct("Device")
286            .field("label", &self.label())
287            .field("limits", &self.limits)
288            .field("features", &self.features)
289            .field("downlevel", &self.downlevel)
290            .finish()
291    }
292}
293
294impl Drop for Device {
295    fn drop(&mut self) {
296        resource_log!("Drop {}", self.error_ident());
297
298        // SAFETY: We are in the Drop impl and we don't use self.zero_buffer anymore after this point.
299        let zero_buffer = unsafe { ManuallyDrop::take(&mut self.zero_buffer) };
300        // SAFETY: We are in the Drop impl and we don't use
301        // self.default_external_texture_params_buffer anymore after this point.
302        let default_external_texture_params_buffer =
303            unsafe { ManuallyDrop::take(&mut self.default_external_texture_params_buffer) };
304        // SAFETY: We are in the Drop impl and we don't use self.fence anymore after this point.
305        let fence = unsafe { ManuallyDrop::take(&mut self.fence.write()) };
306        if let Some(indirect_validation) = self.indirect_validation.take() {
307            indirect_validation.dispose(self.raw.as_ref());
308        }
309        if let Some(timestamp_normalizer) = self.timestamp_normalizer.take() {
310            timestamp_normalizer.dispose(self.raw.as_ref());
311        }
312        unsafe {
313            self.raw.destroy_buffer(zero_buffer);
314            self.raw
315                .destroy_buffer(default_external_texture_params_buffer);
316            self.raw.destroy_fence(fence);
317        }
318    }
319}
320
321impl Device {
322    pub(crate) fn raw(&self) -> &dyn hal::DynDevice {
323        self.raw.as_ref()
324    }
325    pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {
326        if self.features.contains(feature) {
327            Ok(())
328        } else {
329            Err(MissingFeatures(feature))
330        }
331    }
332
333    pub(crate) fn require_downlevel_flags(
334        &self,
335        flags: wgt::DownlevelFlags,
336    ) -> Result<(), MissingDownlevelFlags> {
337        if self.downlevel.flags.contains(flags) {
338            Ok(())
339        } else {
340            Err(MissingDownlevelFlags(flags))
341        }
342    }
343
344    /// # Safety
345    ///
346    /// - See [wgpu::Device::start_graphics_debugger_capture][api] for details the safety.
347    ///
348    /// [api]: ../../wgpu/struct.Device.html#method.start_graphics_debugger_capture
349    pub unsafe fn start_graphics_debugger_capture(&self) {
350        api_log!("Device::start_graphics_debugger_capture");
351
352        if !self.is_valid() {
353            return;
354        }
355        unsafe { self.raw().start_graphics_debugger_capture() };
356    }
357
358    /// # Safety
359    ///
360    /// - See [wgpu::Device::stop_graphics_debugger_capture][api] for details the safety.
361    ///
362    /// [api]: ../../wgpu/struct.Device.html#method.stop_graphics_debugger_capture
363    pub unsafe fn stop_graphics_debugger_capture(&self) {
364        api_log!("Device::stop_graphics_debugger_capture");
365
366        if !self.is_valid() {
367            return;
368        }
369        unsafe { self.raw().stop_graphics_debugger_capture() };
370    }
371}
372
373impl Device {
374    pub(crate) fn new(
375        raw_device: Box<dyn hal::DynDevice>,
376        adapter: &Arc<Adapter>,
377        desc: &DeviceDescriptor,
378        instance_flags: wgt::InstanceFlags,
379    ) -> Result<Self, DeviceError> {
380        #[cfg(not(feature = "trace"))]
381        match &desc.trace {
382            wgt::Trace::Off => {}
383            _ => {
384                log::error!("wgpu-core feature 'trace' is not enabled");
385            }
386        };
387        #[cfg(feature = "trace")]
388        let trace_dir_name: Option<&std::path::PathBuf> = match &desc.trace {
389            wgt::Trace::Off => None,
390            wgt::Trace::Directory(d) => Some(d),
391            // The enum is non_exhaustive, so we must have a fallback arm (that should be
392            // unreachable in practice).
393            t => {
394                log::error!("unimplemented wgpu_types::Trace variant {t:?}");
395                None
396            }
397        };
398
399        let fence = unsafe { raw_device.create_fence() }.map_err(DeviceError::from_hal)?;
400
401        let command_allocator = command::CommandAllocator::new();
402
403        let rt_uses = if desc
404            .required_features
405            .intersects(wgt::Features::EXPERIMENTAL_RAY_QUERY)
406        {
407            wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT
408        } else {
409            wgt::BufferUses::empty()
410        };
411
412        // Create zeroed buffer used for texture clears (and raytracing if required).
413        let zero_buffer = unsafe {
414            raw_device.create_buffer(&hal::BufferDescriptor {
415                label: hal_label(Some("(wgpu internal) zero init buffer"), instance_flags),
416                size: ZERO_BUFFER_SIZE,
417                usage: wgt::BufferUses::COPY_SRC | wgt::BufferUses::COPY_DST | rt_uses,
418                memory_flags: hal::MemoryFlags::empty(),
419            })
420        }
421        .map_err(DeviceError::from_hal)?;
422
423        let default_external_texture_params_buffer = unsafe {
424            raw_device.create_buffer(&hal::BufferDescriptor {
425                label: hal_label(
426                    Some("(wgpu internal) default external texture params buffer"),
427                    instance_flags,
428                ),
429                size: size_of::<ExternalTextureParams>() as _,
430                usage: wgt::BufferUses::COPY_DST | wgt::BufferUses::UNIFORM,
431                memory_flags: hal::MemoryFlags::empty(),
432            })
433        }
434        .map_err(DeviceError::from_hal)?;
435
436        let alignments = adapter.raw.capabilities.alignments.clone();
437        let downlevel = adapter.raw.capabilities.downlevel.clone();
438
439        let enable_indirect_validation = instance_flags
440            .contains(wgt::InstanceFlags::VALIDATION_INDIRECT_CALL)
441            && downlevel
442                .flags
443                .contains(wgt::DownlevelFlags::INDIRECT_EXECUTION);
444
445        let indirect_validation = if enable_indirect_validation {
446            Some(crate::indirect_validation::IndirectValidation::new(
447                raw_device.as_ref(),
448                &desc.required_limits,
449                &desc.required_features,
450                adapter.backend(),
451            )?)
452        } else {
453            None
454        };
455
456        Ok(Self {
457            raw: raw_device,
458            adapter: adapter.clone(),
459            queue: OnceCellOrLock::new(),
460            zero_buffer: ManuallyDrop::new(zero_buffer),
461            default_external_texture_params_buffer: ManuallyDrop::new(
462                default_external_texture_params_buffer,
463            ),
464            label: desc.label.to_string(),
465            command_allocator,
466            command_indices: RwLock::new(
467                rank::DEVICE_COMMAND_INDICES,
468                CommandIndices {
469                    active_submission_index: 0,
470                    // By starting at one, we can put the result in a NonZeroU64.
471                    next_acceleration_structure_build_command_index: 1,
472                },
473            ),
474            last_successful_submission_index: AtomicU64::new(0),
475            fence: RwLock::new(rank::DEVICE_FENCE, ManuallyDrop::new(fence)),
476            snatchable_lock: unsafe { SnatchLock::new(rank::DEVICE_SNATCHABLE_LOCK) },
477            valid: AtomicBool::new(true),
478            device_lost_closure: Mutex::new(rank::DEVICE_LOST_CLOSURE, None),
479            trackers: Mutex::new(rank::DEVICE_TRACKERS, DeviceTracker::new()),
480            tracker_indices: TrackerIndexAllocators::new(),
481            bgl_pool: ResourcePool::new(),
482            #[cfg(feature = "trace")]
483            trace: Mutex::new(
484                rank::DEVICE_TRACE,
485                trace_dir_name.and_then(|path| match trace::Trace::new(path.clone()) {
486                    Ok(mut trace) => {
487                        trace.add(trace::Action::Init {
488                            desc: wgt::DeviceDescriptor {
489                                trace: wgt::Trace::Off,
490                                ..desc.clone()
491                            },
492                            backend: adapter.backend(),
493                        });
494                        Some(trace)
495                    }
496                    Err(e) => {
497                        log::error!("Unable to start a trace in '{path:?}': {e}");
498                        None
499                    }
500                }),
501            ),
502            alignments,
503            limits: desc.required_limits.clone(),
504            features: desc.required_features,
505            downlevel,
506            instance_flags,
507            deferred_destroy: Mutex::new(rank::DEVICE_DEFERRED_DESTROY, Vec::new()),
508            usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()),
509            timestamp_normalizer: OnceCellOrLock::new(),
510            indirect_validation,
511        })
512    }
513
514    /// Initializes [`Device::default_external_texture_params_buffer`] with
515    /// required values such that a [`TextureView`] bound to a
516    /// [`wgt::BindingType::ExternalTexture`] binding point will be rendered
517    /// correctly.
518    fn init_default_external_texture_params_buffer(self: &Arc<Self>) -> Result<(), DeviceError> {
519        let data = ExternalTextureParams {
520            #[rustfmt::skip]
521            yuv_conversion_matrix: [
522                1.0, 0.0, 0.0, 0.0,
523                0.0, 1.0, 0.0, 0.0,
524                0.0, 0.0, 1.0, 0.0,
525                0.0, 0.0, 0.0, 1.0,
526            ],
527            #[rustfmt::skip]
528            gamut_conversion_matrix: [
529                1.0, 0.0, 0.0, /* padding */ 0.0,
530                0.0, 1.0, 0.0, /* padding */ 0.0,
531                0.0, 0.0, 1.0, /* padding */ 0.0,
532            ],
533            src_transfer_function: Default::default(),
534            dst_transfer_function: Default::default(),
535            size: [0, 0],
536            #[rustfmt::skip]
537            sample_transform: [
538                1.0, 0.0,
539                0.0, 1.0,
540                0.0, 0.0
541            ],
542            #[rustfmt::skip]
543            load_transform: [
544                1.0, 0.0,
545                0.0, 1.0,
546                0.0, 0.0
547            ],
548            num_planes: 1,
549            _padding: Default::default(),
550        };
551        let mut staging_buffer =
552            StagingBuffer::new(self, wgt::BufferSize::new(size_of_val(&data) as _).unwrap())?;
553        staging_buffer.write(bytemuck::bytes_of(&data));
554        let staging_buffer = staging_buffer.flush();
555
556        let params_buffer = self.default_external_texture_params_buffer.as_ref();
557        let queue = self.get_queue().unwrap();
558        let mut pending_writes = queue.pending_writes.lock();
559
560        unsafe {
561            pending_writes
562                .command_encoder
563                .transition_buffers(&[hal::BufferBarrier {
564                    buffer: params_buffer,
565                    usage: hal::StateTransition {
566                        from: wgt::BufferUses::MAP_WRITE,
567                        to: wgt::BufferUses::COPY_SRC,
568                    },
569                }]);
570            pending_writes.command_encoder.copy_buffer_to_buffer(
571                staging_buffer.raw(),
572                params_buffer,
573                &[hal::BufferCopy {
574                    src_offset: 0,
575                    dst_offset: 0,
576                    size: staging_buffer.size,
577                }],
578            );
579            pending_writes.consume(staging_buffer);
580            pending_writes
581                .command_encoder
582                .transition_buffers(&[hal::BufferBarrier {
583                    buffer: params_buffer,
584                    usage: hal::StateTransition {
585                        from: wgt::BufferUses::COPY_DST,
586                        to: wgt::BufferUses::UNIFORM,
587                    },
588                }]);
589        }
590
591        Ok(())
592    }
593
594    pub fn late_init_resources_with_queue(self: &Arc<Self>) -> Result<(), RequestDeviceError> {
595        let queue = self.get_queue().unwrap();
596
597        let timestamp_normalizer = crate::timestamp_normalization::TimestampNormalizer::new(
598            self,
599            queue.get_timestamp_period(),
600        )?;
601
602        self.timestamp_normalizer
603            .set(timestamp_normalizer)
604            .unwrap_or_else(|_| panic!("Called late_init_resources_with_queue twice"));
605
606        self.init_default_external_texture_params_buffer()?;
607
608        Ok(())
609    }
610
611    /// Returns the backend this device is using.
612    pub fn backend(&self) -> wgt::Backend {
613        self.adapter.backend()
614    }
615
616    pub fn is_valid(&self) -> bool {
617        self.valid.load(Ordering::Acquire)
618    }
619
620    pub fn check_is_valid(&self) -> Result<(), DeviceError> {
621        if self.is_valid() {
622            Ok(())
623        } else {
624            Err(DeviceError::Lost)
625        }
626    }
627
628    /// Checks that we are operating within the memory budget reported by the native APIs.
629    ///
630    /// If we are not, the device gets invalidated.
631    ///
632    /// The budget might fluctuate over the lifetime of the application, so it should be checked
633    /// somewhat frequently.
634    pub fn lose_if_oom(&self) {
635        let _ = self
636            .raw()
637            .check_if_oom()
638            .map_err(|e| self.handle_hal_error(e));
639    }
640
641    pub fn handle_hal_error(&self, error: hal::DeviceError) -> DeviceError {
642        match error {
643            hal::DeviceError::OutOfMemory
644            | hal::DeviceError::Lost
645            | hal::DeviceError::Unexpected => {
646                self.lose(&error.to_string());
647            }
648        }
649        DeviceError::from_hal(error)
650    }
651
652    pub fn handle_hal_error_with_nonfatal_oom(&self, error: hal::DeviceError) -> DeviceError {
653        match error {
654            hal::DeviceError::OutOfMemory => DeviceError::from_hal(error),
655            error => self.handle_hal_error(error),
656        }
657    }
658
659    /// Run some destroy operations that were deferred.
660    ///
661    /// Destroying the resources requires taking a write lock on the device's snatch lock,
662    /// so a good reason for deferring resource destruction is when we don't know for sure
663    /// how risky it is to take the lock (typically, it shouldn't be taken from the drop
664    /// implementation of a reference-counted structure).
665    /// The snatch lock must not be held while this function is called.
666    pub(crate) fn deferred_resource_destruction(&self) {
667        let deferred_destroy = mem::take(&mut *self.deferred_destroy.lock());
668        for item in deferred_destroy {
669            match item {
670                DeferredDestroy::TextureViews(views) => {
671                    for view in views {
672                        let Some(view) = view.upgrade() else {
673                            continue;
674                        };
675                        let Some(raw_view) = view.raw.snatch(&mut self.snatchable_lock.write())
676                        else {
677                            continue;
678                        };
679
680                        resource_log!("Destroy raw {}", view.error_ident());
681
682                        unsafe {
683                            self.raw().destroy_texture_view(raw_view);
684                        }
685                    }
686                }
687                DeferredDestroy::BindGroups(bind_groups) => {
688                    for bind_group in bind_groups {
689                        let Some(bind_group) = bind_group.upgrade() else {
690                            continue;
691                        };
692                        let Some(raw_bind_group) =
693                            bind_group.raw.snatch(&mut self.snatchable_lock.write())
694                        else {
695                            continue;
696                        };
697
698                        resource_log!("Destroy raw {}", bind_group.error_ident());
699
700                        unsafe {
701                            self.raw().destroy_bind_group(raw_bind_group);
702                        }
703                    }
704                }
705            }
706        }
707    }
708
709    pub fn get_queue(&self) -> Option<Arc<Queue>> {
710        self.queue.get().as_ref()?.upgrade()
711    }
712
713    pub fn set_queue(&self, queue: &Arc<Queue>) {
714        assert!(self.queue.set(Arc::downgrade(queue)).is_ok());
715    }
716
717    pub fn poll(
718        &self,
719        poll_type: wgt::PollType<crate::SubmissionIndex>,
720    ) -> Result<wgt::PollStatus, WaitIdleError> {
721        let (user_closures, result) = self.poll_and_return_closures(poll_type);
722        user_closures.fire();
723        result
724    }
725
726    /// Poll the device, returning any `UserClosures` that need to be executed.
727    ///
728    /// The caller must invoke the `UserClosures` even if this function returns
729    /// an error. This is an internal helper, used by `Device::poll` and
730    /// `Global::poll_all_devices`, so that `poll_all_devices` can invoke
731    /// closures once after all devices have been polled.
732    pub(crate) fn poll_and_return_closures(
733        &self,
734        poll_type: wgt::PollType<crate::SubmissionIndex>,
735    ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {
736        let snatch_guard = self.snatchable_lock.read();
737        let fence = self.fence.read();
738        let maintain_result = self.maintain(fence, poll_type, snatch_guard);
739
740        self.lose_if_oom();
741
742        // Some deferred destroys are scheduled in maintain so run this right after
743        // to avoid holding on to them until the next device poll.
744        self.deferred_resource_destruction();
745
746        maintain_result
747    }
748
749    /// Check the current status of the GPU and process any submissions that have
750    /// finished.
751    ///
752    /// The `poll_type` argument tells if this function should wait for a particular
753    /// submission index to complete, or if it should just poll the current status.
754    ///
755    /// This will process _all_ completed submissions, even if the caller only asked
756    /// us to poll to a given submission index.
757    ///
758    /// Return a pair `(closures, result)`, where:
759    ///
760    /// - `closures` is a list of callbacks that need to be invoked informing the user
761    ///   about various things occurring. These happen and should be handled even if
762    ///   this function returns an error, hence they are outside of the result.
763    ///
764    /// - `results` is a boolean indicating the result of the wait operation, including
765    ///   if there was a timeout or a validation error.
766    pub(crate) fn maintain<'this>(
767        &'this self,
768        fence: crate::lock::RwLockReadGuard<ManuallyDrop<Box<dyn hal::DynFence>>>,
769        poll_type: wgt::PollType<crate::SubmissionIndex>,
770        snatch_guard: SnatchGuard,
771    ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {
772        profiling::scope!("Device::maintain");
773
774        let mut user_closures = UserClosures::default();
775
776        // If a wait was requested, determine which submission index to wait for.
777        let wait_submission_index = match poll_type {
778            wgt::PollType::Wait {
779                submission_index: Some(submission_index),
780                ..
781            } => {
782                let last_successful_submission_index = self
783                    .last_successful_submission_index
784                    .load(Ordering::Acquire);
785
786                if submission_index > last_successful_submission_index {
787                    let result = Err(WaitIdleError::WrongSubmissionIndex(
788                        submission_index,
789                        last_successful_submission_index,
790                    ));
791
792                    return (user_closures, result);
793                }
794
795                Some(submission_index)
796            }
797            wgt::PollType::Wait {
798                submission_index: None,
799                ..
800            } => Some(
801                self.last_successful_submission_index
802                    .load(Ordering::Acquire),
803            ),
804            wgt::PollType::Poll => None,
805        };
806
807        // Wait for the submission index if requested.
808        if let Some(target_submission_index) = wait_submission_index {
809            log::trace!("Device::maintain: waiting for submission index {target_submission_index}");
810
811            let wait_timeout = match poll_type {
812                wgt::PollType::Wait { timeout, .. } => timeout,
813                wgt::PollType::Poll => unreachable!(
814                    "`wait_submission_index` index for poll type `Poll` should be None"
815                ),
816            };
817
818            let wait_result = unsafe {
819                self.raw()
820                    .wait(fence.as_ref(), target_submission_index, wait_timeout)
821            };
822
823            // This error match is only about `DeviceErrors`. At this stage we do not care if
824            // the wait succeeded or not, and the `Ok(bool)`` variant is ignored.
825            if let Err(e) = wait_result {
826                let hal_error: WaitIdleError = self.handle_hal_error(e).into();
827                return (user_closures, Err(hal_error));
828            }
829        }
830
831        // Get the currently finished submission index. This may be higher than the requested
832        // wait, or it may be less than the requested wait if the wait failed.
833        let fence_value_result = unsafe { self.raw().get_fence_value(fence.as_ref()) };
834        let current_finished_submission = match fence_value_result {
835            Ok(fence_value) => fence_value,
836            Err(e) => {
837                let hal_error: WaitIdleError = self.handle_hal_error(e).into();
838                return (user_closures, Err(hal_error));
839            }
840        };
841
842        // Maintain all finished submissions on the queue, updating the relevant user closures and collecting if the queue is empty.
843        //
844        // We don't use the result of the wait here, as we want to progress forward as far as possible
845        // and the wait could have been for submissions that finished long ago.
846        let mut queue_empty = false;
847        if let Some(queue) = self.get_queue() {
848            let queue_result = queue.maintain(current_finished_submission, &snatch_guard);
849            (
850                user_closures.submissions,
851                user_closures.mappings,
852                user_closures.blas_compact_ready,
853                queue_empty,
854            ) = queue_result
855        };
856
857        // Based on the queue empty status, and the current finished submission index, determine the result of the poll.
858        let result = if queue_empty {
859            if let Some(wait_submission_index) = wait_submission_index {
860                // Assert to ensure that if we received a queue empty status, the fence shows the correct value.
861                // This is defensive, as this should never be hit.
862                assert!(
863                    current_finished_submission >= wait_submission_index,
864                    "If the queue is empty, the current submission index ({current_finished_submission}) should be at least the wait submission index ({wait_submission_index})"
865                );
866            }
867
868            Ok(wgt::PollStatus::QueueEmpty)
869        } else if let Some(wait_submission_index) = wait_submission_index {
870            // This is theoretically possible to succeed more than checking on the poll result
871            // as submissions could have finished in the time between the timeout resolving,
872            // the thread getting scheduled again, and us checking the fence value.
873            if current_finished_submission >= wait_submission_index {
874                Ok(wgt::PollStatus::WaitSucceeded)
875            } else {
876                Err(WaitIdleError::Timeout)
877            }
878        } else {
879            Ok(wgt::PollStatus::Poll)
880        };
881
882        // Detect if we have been destroyed and now need to lose the device.
883        //
884        // If we are invalid (set at start of destroy) and our queue is empty,
885        // and we have a DeviceLostClosure, return the closure to be called by
886        // our caller. This will complete the steps for both destroy and for
887        // "lose the device".
888        let mut should_release_gpu_resource = false;
889        if !self.is_valid() && queue_empty {
890            // We can release gpu resources associated with this device (but not
891            // while holding the life_tracker lock).
892            should_release_gpu_resource = true;
893
894            // If we have a DeviceLostClosure, build an invocation with the
895            // reason DeviceLostReason::Destroyed and no message.
896            if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
897                user_closures
898                    .device_lost_invocations
899                    .push(DeviceLostInvocation {
900                        closure: device_lost_closure,
901                        reason: DeviceLostReason::Destroyed,
902                        message: String::new(),
903                    });
904            }
905        }
906
907        // Don't hold the locks while calling release_gpu_resources.
908        drop(fence);
909        drop(snatch_guard);
910
911        if should_release_gpu_resource {
912            self.release_gpu_resources();
913        }
914
915        (user_closures, result)
916    }
917
918    pub fn create_buffer(
919        self: &Arc<Self>,
920        desc: &resource::BufferDescriptor,
921    ) -> Result<Arc<Buffer>, resource::CreateBufferError> {
922        self.check_is_valid()?;
923
924        if desc.size > self.limits.max_buffer_size {
925            return Err(resource::CreateBufferError::MaxBufferSize {
926                requested: desc.size,
927                maximum: self.limits.max_buffer_size,
928            });
929        }
930
931        if desc
932            .usage
933            .intersects(wgt::BufferUsages::BLAS_INPUT | wgt::BufferUsages::TLAS_INPUT)
934        {
935            self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)?;
936        }
937
938        if desc.usage.contains(wgt::BufferUsages::INDEX)
939            && desc.usage.contains(
940                wgt::BufferUsages::VERTEX
941                    | wgt::BufferUsages::UNIFORM
942                    | wgt::BufferUsages::INDIRECT
943                    | wgt::BufferUsages::STORAGE,
944            )
945        {
946            self.require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)?;
947        }
948
949        if desc.usage.is_empty() || desc.usage.contains_unknown_bits() {
950            return Err(resource::CreateBufferError::InvalidUsage(desc.usage));
951        }
952
953        if !self
954            .features
955            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
956        {
957            use wgt::BufferUsages as Bu;
958            let write_mismatch = desc.usage.contains(Bu::MAP_WRITE)
959                && !(Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage);
960            let read_mismatch = desc.usage.contains(Bu::MAP_READ)
961                && !(Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage);
962            if write_mismatch || read_mismatch {
963                return Err(resource::CreateBufferError::UsageMismatch(desc.usage));
964            }
965        }
966
967        let mut usage = conv::map_buffer_usage(desc.usage);
968
969        if desc.usage.contains(wgt::BufferUsages::INDIRECT) {
970            self.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
971            // We are going to be reading from it, internally;
972            // when validating the content of the buffer
973            usage |= wgt::BufferUses::STORAGE_READ_ONLY | wgt::BufferUses::STORAGE_READ_WRITE;
974        }
975
976        if desc.usage.contains(wgt::BufferUsages::QUERY_RESOLVE) {
977            usage |= TIMESTAMP_NORMALIZATION_BUFFER_USES;
978        }
979
980        if desc.mapped_at_creation {
981            if desc.size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
982                return Err(resource::CreateBufferError::UnalignedSize);
983            }
984            if !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
985                // we are going to be copying into it, internally
986                usage |= wgt::BufferUses::COPY_DST;
987            }
988        } else {
989            // We are required to zero out (initialize) all memory. This is done
990            // on demand using clear_buffer which requires write transfer usage!
991            usage |= wgt::BufferUses::COPY_DST;
992        }
993
994        let actual_size = if desc.size == 0 {
995            wgt::COPY_BUFFER_ALIGNMENT
996        } else if desc.usage.contains(wgt::BufferUsages::VERTEX) {
997            // Bumping the size by 1 so that we can bind an empty range at the
998            // end of the buffer.
999            desc.size + 1
1000        } else {
1001            desc.size
1002        };
1003        let clear_remainder = actual_size % wgt::COPY_BUFFER_ALIGNMENT;
1004        let aligned_size = if clear_remainder != 0 {
1005            actual_size + wgt::COPY_BUFFER_ALIGNMENT - clear_remainder
1006        } else {
1007            actual_size
1008        };
1009
1010        let hal_desc = hal::BufferDescriptor {
1011            label: desc.label.to_hal(self.instance_flags),
1012            size: aligned_size,
1013            usage,
1014            memory_flags: hal::MemoryFlags::empty(),
1015        };
1016        let buffer = unsafe { self.raw().create_buffer(&hal_desc) }
1017            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
1018
1019        let timestamp_normalization_bind_group = Snatchable::new(unsafe {
1020            // SAFETY: The size passed here must not overflow the buffer.
1021            self.timestamp_normalizer
1022                .get()
1023                .unwrap()
1024                .create_normalization_bind_group(
1025                    self,
1026                    &*buffer,
1027                    desc.label.as_deref(),
1028                    wgt::BufferSize::new(hal_desc.size).unwrap(),
1029                    desc.usage,
1030                )
1031        }?);
1032
1033        let indirect_validation_bind_groups =
1034            self.create_indirect_validation_bind_groups(buffer.as_ref(), desc.size, desc.usage)?;
1035
1036        let buffer = Buffer {
1037            raw: Snatchable::new(buffer),
1038            device: self.clone(),
1039            usage: desc.usage,
1040            size: desc.size,
1041            initialization_status: RwLock::new(
1042                rank::BUFFER_INITIALIZATION_STATUS,
1043                BufferInitTracker::new(aligned_size),
1044            ),
1045            map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
1046            label: desc.label.to_string(),
1047            tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),
1048            bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
1049            timestamp_normalization_bind_group,
1050            indirect_validation_bind_groups,
1051        };
1052
1053        let buffer = Arc::new(buffer);
1054
1055        let buffer_use = if !desc.mapped_at_creation {
1056            wgt::BufferUses::empty()
1057        } else if desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
1058            // buffer is mappable, so we are just doing that at start
1059            let map_size = buffer.size;
1060            let mapping = if map_size == 0 {
1061                hal::BufferMapping {
1062                    ptr: core::ptr::NonNull::dangling(),
1063                    is_coherent: true,
1064                }
1065            } else {
1066                let snatch_guard: SnatchGuard = self.snatchable_lock.read();
1067                map_buffer(&buffer, 0, map_size, HostMap::Write, &snatch_guard)?
1068            };
1069            *buffer.map_state.lock() = resource::BufferMapState::Active {
1070                mapping,
1071                range: 0..map_size,
1072                host: HostMap::Write,
1073            };
1074            wgt::BufferUses::MAP_WRITE
1075        } else {
1076            let mut staging_buffer =
1077                StagingBuffer::new(self, wgt::BufferSize::new(aligned_size).unwrap())?;
1078
1079            // Zero initialize memory and then mark the buffer as initialized
1080            // (it's guaranteed that this is the case by the time the buffer is usable)
1081            staging_buffer.write_zeros();
1082            buffer.initialization_status.write().drain(0..aligned_size);
1083
1084            *buffer.map_state.lock() = resource::BufferMapState::Init { staging_buffer };
1085            wgt::BufferUses::COPY_DST
1086        };
1087
1088        self.trackers
1089            .lock()
1090            .buffers
1091            .insert_single(&buffer, buffer_use);
1092
1093        Ok(buffer)
1094    }
1095
1096    #[cfg(feature = "replay")]
1097    pub fn set_buffer_data(
1098        self: &Arc<Self>,
1099        buffer: &Arc<Buffer>,
1100        offset: wgt::BufferAddress,
1101        data: &[u8],
1102    ) -> resource::BufferAccessResult {
1103        use crate::resource::RawResourceAccess;
1104
1105        let device = &buffer.device;
1106
1107        device.check_is_valid()?;
1108        buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?;
1109
1110        let last_submission = device
1111            .get_queue()
1112            .and_then(|queue| queue.lock_life().get_buffer_latest_submission_index(buffer));
1113
1114        if let Some(last_submission) = last_submission {
1115            device.wait_for_submit(last_submission)?;
1116        }
1117
1118        let snatch_guard = device.snatchable_lock.read();
1119        let raw_buf = buffer.try_raw(&snatch_guard)?;
1120
1121        let mapping = unsafe {
1122            device
1123                .raw()
1124                .map_buffer(raw_buf, offset..offset + data.len() as u64)
1125        }
1126        .map_err(|e| device.handle_hal_error(e))?;
1127
1128        unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()) };
1129
1130        if !mapping.is_coherent {
1131            #[allow(clippy::single_range_in_vec_init)]
1132            unsafe {
1133                device
1134                    .raw()
1135                    .flush_mapped_ranges(raw_buf, &[offset..offset + data.len() as u64])
1136            };
1137        }
1138
1139        unsafe { device.raw().unmap_buffer(raw_buf) };
1140
1141        Ok(())
1142    }
1143
1144    pub(crate) fn create_texture_from_hal(
1145        self: &Arc<Self>,
1146        hal_texture: Box<dyn hal::DynTexture>,
1147        desc: &resource::TextureDescriptor,
1148    ) -> Result<Arc<Texture>, resource::CreateTextureError> {
1149        let format_features = self
1150            .describe_format_features(desc.format)
1151            .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?;
1152
1153        unsafe { self.raw().add_raw_texture(&*hal_texture) };
1154
1155        let texture = Texture::new(
1156            self,
1157            resource::TextureInner::Native { raw: hal_texture },
1158            conv::map_texture_usage(desc.usage, desc.format.into(), format_features.flags),
1159            desc,
1160            format_features,
1161            resource::TextureClearMode::None,
1162            false,
1163        );
1164
1165        let texture = Arc::new(texture);
1166
1167        self.trackers
1168            .lock()
1169            .textures
1170            .insert_single(&texture, wgt::TextureUses::UNINITIALIZED);
1171
1172        Ok(texture)
1173    }
1174
1175    /// # Safety
1176    ///
1177    /// - `hal_buffer` must have been created on this device.
1178    /// - `hal_buffer` must have been created respecting `desc` (in particular, the size).
1179    /// - `hal_buffer` must be initialized.
1180    /// - `hal_buffer` must not have zero size.
1181    pub(crate) unsafe fn create_buffer_from_hal(
1182        self: &Arc<Self>,
1183        hal_buffer: Box<dyn hal::DynBuffer>,
1184        desc: &resource::BufferDescriptor,
1185    ) -> (Fallible<Buffer>, Option<resource::CreateBufferError>) {
1186        let timestamp_normalization_bind_group = unsafe {
1187            match self
1188                .timestamp_normalizer
1189                .get()
1190                .unwrap()
1191                .create_normalization_bind_group(
1192                    self,
1193                    &*hal_buffer,
1194                    desc.label.as_deref(),
1195                    wgt::BufferSize::new(desc.size).unwrap(),
1196                    desc.usage,
1197                ) {
1198                Ok(bg) => Snatchable::new(bg),
1199                Err(e) => {
1200                    return (
1201                        Fallible::Invalid(Arc::new(desc.label.to_string())),
1202                        Some(e.into()),
1203                    )
1204                }
1205            }
1206        };
1207
1208        let indirect_validation_bind_groups = match self.create_indirect_validation_bind_groups(
1209            hal_buffer.as_ref(),
1210            desc.size,
1211            desc.usage,
1212        ) {
1213            Ok(ok) => ok,
1214            Err(e) => return (Fallible::Invalid(Arc::new(desc.label.to_string())), Some(e)),
1215        };
1216
1217        unsafe { self.raw().add_raw_buffer(&*hal_buffer) };
1218
1219        let buffer = Buffer {
1220            raw: Snatchable::new(hal_buffer),
1221            device: self.clone(),
1222            usage: desc.usage,
1223            size: desc.size,
1224            initialization_status: RwLock::new(
1225                rank::BUFFER_INITIALIZATION_STATUS,
1226                BufferInitTracker::new(0),
1227            ),
1228            map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
1229            label: desc.label.to_string(),
1230            tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),
1231            bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
1232            timestamp_normalization_bind_group,
1233            indirect_validation_bind_groups,
1234        };
1235
1236        let buffer = Arc::new(buffer);
1237
1238        self.trackers
1239            .lock()
1240            .buffers
1241            .insert_single(&buffer, wgt::BufferUses::empty());
1242
1243        (Fallible::Valid(buffer), None)
1244    }
1245
1246    fn create_indirect_validation_bind_groups(
1247        &self,
1248        raw_buffer: &dyn hal::DynBuffer,
1249        buffer_size: u64,
1250        usage: wgt::BufferUsages,
1251    ) -> Result<Snatchable<crate::indirect_validation::BindGroups>, resource::CreateBufferError>
1252    {
1253        if !usage.contains(wgt::BufferUsages::INDIRECT) {
1254            return Ok(Snatchable::empty());
1255        }
1256
1257        let Some(ref indirect_validation) = self.indirect_validation else {
1258            return Ok(Snatchable::empty());
1259        };
1260
1261        let bind_groups = crate::indirect_validation::BindGroups::new(
1262            indirect_validation,
1263            self,
1264            buffer_size,
1265            raw_buffer,
1266        )
1267        .map_err(resource::CreateBufferError::IndirectValidationBindGroup)?;
1268
1269        if let Some(bind_groups) = bind_groups {
1270            Ok(Snatchable::new(bind_groups))
1271        } else {
1272            Ok(Snatchable::empty())
1273        }
1274    }
1275
1276    pub fn create_texture(
1277        self: &Arc<Self>,
1278        desc: &resource::TextureDescriptor,
1279    ) -> Result<Arc<Texture>, resource::CreateTextureError> {
1280        use resource::{CreateTextureError, TextureDimensionError};
1281
1282        self.check_is_valid()?;
1283
1284        if desc.usage.is_empty() || desc.usage.contains_unknown_bits() {
1285            return Err(CreateTextureError::InvalidUsage(desc.usage));
1286        }
1287
1288        conv::check_texture_dimension_size(
1289            desc.dimension,
1290            desc.size,
1291            desc.sample_count,
1292            &self.limits,
1293        )?;
1294
1295        if desc.dimension != wgt::TextureDimension::D2 {
1296            // Depth textures can only be 2D
1297            if desc.format.is_depth_stencil_format() {
1298                return Err(CreateTextureError::InvalidDepthDimension(
1299                    desc.dimension,
1300                    desc.format,
1301                ));
1302            }
1303        }
1304
1305        if desc.dimension != wgt::TextureDimension::D2
1306            && desc.dimension != wgt::TextureDimension::D3
1307        {
1308            // Compressed textures can only be 2D or 3D
1309            if desc.format.is_compressed() {
1310                return Err(CreateTextureError::InvalidCompressedDimension(
1311                    desc.dimension,
1312                    desc.format,
1313                ));
1314            }
1315
1316            // Renderable textures can only be 2D or 3D
1317            if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1318                return Err(CreateTextureError::InvalidDimensionUsages(
1319                    wgt::TextureUsages::RENDER_ATTACHMENT,
1320                    desc.dimension,
1321                ));
1322            }
1323        }
1324
1325        if desc.format.is_compressed() {
1326            let (block_width, block_height) = desc.format.block_dimensions();
1327
1328            if desc.size.width % block_width != 0 {
1329                return Err(CreateTextureError::InvalidDimension(
1330                    TextureDimensionError::NotMultipleOfBlockWidth {
1331                        width: desc.size.width,
1332                        block_width,
1333                        format: desc.format,
1334                    },
1335                ));
1336            }
1337
1338            if desc.size.height % block_height != 0 {
1339                return Err(CreateTextureError::InvalidDimension(
1340                    TextureDimensionError::NotMultipleOfBlockHeight {
1341                        height: desc.size.height,
1342                        block_height,
1343                        format: desc.format,
1344                    },
1345                ));
1346            }
1347
1348            if desc.dimension == wgt::TextureDimension::D3 {
1349                // Only BCn formats with Sliced 3D feature can be used for 3D textures
1350                if desc.format.is_bcn() {
1351                    self.require_features(wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D)
1352                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1353                } else if desc.format.is_astc() {
1354                    self.require_features(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D)
1355                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1356                } else {
1357                    return Err(CreateTextureError::InvalidCompressedDimension(
1358                        desc.dimension,
1359                        desc.format,
1360                    ));
1361                }
1362            }
1363        }
1364
1365        let mips = desc.mip_level_count;
1366        let max_levels_allowed = desc.size.max_mips(desc.dimension).min(hal::MAX_MIP_LEVELS);
1367        if mips == 0 || mips > max_levels_allowed {
1368            return Err(CreateTextureError::InvalidMipLevelCount {
1369                requested: mips,
1370                maximum: max_levels_allowed,
1371            });
1372        }
1373
1374        {
1375            let (mut width_multiple, mut height_multiple) = desc.format.size_multiple_requirement();
1376
1377            if desc.format.is_multi_planar_format() {
1378                // TODO(https://github.com/gfx-rs/wgpu/issues/8491): fix
1379                // `mip_level_size` calculation for these formats and relax this
1380                // restriction.
1381                width_multiple <<= desc.mip_level_count.saturating_sub(1);
1382                height_multiple <<= desc.mip_level_count.saturating_sub(1);
1383            }
1384
1385            if desc.size.width % width_multiple != 0 {
1386                return Err(CreateTextureError::InvalidDimension(
1387                    TextureDimensionError::WidthNotMultipleOf {
1388                        width: desc.size.width,
1389                        multiple: width_multiple,
1390                        format: desc.format,
1391                    },
1392                ));
1393            }
1394
1395            if desc.size.height % height_multiple != 0 {
1396                return Err(CreateTextureError::InvalidDimension(
1397                    TextureDimensionError::HeightNotMultipleOf {
1398                        height: desc.size.height,
1399                        multiple: height_multiple,
1400                        format: desc.format,
1401                    },
1402                ));
1403            }
1404        }
1405
1406        if desc.usage.contains(wgt::TextureUsages::TRANSIENT) {
1407            if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1408                return Err(CreateTextureError::InvalidUsage(
1409                    wgt::TextureUsages::TRANSIENT,
1410                ));
1411            }
1412            let extra_usage =
1413                desc.usage - wgt::TextureUsages::TRANSIENT - wgt::TextureUsages::RENDER_ATTACHMENT;
1414            if !extra_usage.is_empty() {
1415                return Err(CreateTextureError::IncompatibleUsage(
1416                    wgt::TextureUsages::TRANSIENT,
1417                    extra_usage,
1418                ));
1419            }
1420        }
1421
1422        let format_features = self
1423            .describe_format_features(desc.format)
1424            .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1425
1426        if desc.sample_count > 1 {
1427            if desc.mip_level_count != 1 {
1428                return Err(CreateTextureError::InvalidMipLevelCount {
1429                    requested: desc.mip_level_count,
1430                    maximum: 1,
1431                });
1432            }
1433
1434            if desc.size.depth_or_array_layers != 1 {
1435                return Err(CreateTextureError::InvalidDimension(
1436                    TextureDimensionError::MultisampledDepthOrArrayLayer(
1437                        desc.size.depth_or_array_layers,
1438                    ),
1439                ));
1440            }
1441
1442            if desc.usage.contains(wgt::TextureUsages::STORAGE_BINDING) {
1443                return Err(CreateTextureError::InvalidMultisampledStorageBinding);
1444            }
1445
1446            if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1447                return Err(CreateTextureError::MultisampledNotRenderAttachment);
1448            }
1449
1450            if !format_features.flags.intersects(
1451                wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4
1452                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2
1453                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8
1454                    | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1455            ) {
1456                return Err(CreateTextureError::InvalidMultisampledFormat(desc.format));
1457            }
1458
1459            if !format_features
1460                .flags
1461                .sample_count_supported(desc.sample_count)
1462            {
1463                return Err(CreateTextureError::InvalidSampleCount(
1464                    desc.sample_count,
1465                    desc.format,
1466                    desc.format
1467                        .guaranteed_format_features(self.features)
1468                        .flags
1469                        .supported_sample_counts(),
1470                    self.adapter
1471                        .get_texture_format_features(desc.format)
1472                        .flags
1473                        .supported_sample_counts(),
1474                ));
1475            };
1476        }
1477
1478        let missing_allowed_usages = match desc.format.planes() {
1479            Some(planes) => {
1480                let mut planes_usages = wgt::TextureUsages::all();
1481                for plane in 0..planes {
1482                    let aspect = wgt::TextureAspect::from_plane(plane).unwrap();
1483                    let format = desc.format.aspect_specific_format(aspect).unwrap();
1484                    let format_features = self
1485                        .describe_format_features(format)
1486                        .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1487
1488                    planes_usages &= format_features.allowed_usages;
1489                }
1490
1491                desc.usage - planes_usages
1492            }
1493            None => desc.usage - format_features.allowed_usages,
1494        };
1495
1496        if !missing_allowed_usages.is_empty() {
1497            // detect downlevel incompatibilities
1498            let wgpu_allowed_usages = desc
1499                .format
1500                .guaranteed_format_features(self.features)
1501                .allowed_usages;
1502            let wgpu_missing_usages = desc.usage - wgpu_allowed_usages;
1503            return Err(CreateTextureError::InvalidFormatUsages(
1504                missing_allowed_usages,
1505                desc.format,
1506                wgpu_missing_usages.is_empty(),
1507            ));
1508        }
1509
1510        let mut hal_view_formats = Vec::new();
1511        for format in desc.view_formats.iter() {
1512            if desc.format == *format {
1513                continue;
1514            }
1515            if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
1516                return Err(CreateTextureError::InvalidViewFormat(*format, desc.format));
1517            }
1518            hal_view_formats.push(*format);
1519        }
1520        if !hal_view_formats.is_empty() {
1521            self.require_downlevel_flags(wgt::DownlevelFlags::VIEW_FORMATS)?;
1522        }
1523
1524        let hal_usage = conv::map_texture_usage_for_texture(desc, &format_features);
1525
1526        let hal_desc = hal::TextureDescriptor {
1527            label: desc.label.to_hal(self.instance_flags),
1528            size: desc.size,
1529            mip_level_count: desc.mip_level_count,
1530            sample_count: desc.sample_count,
1531            dimension: desc.dimension,
1532            format: desc.format,
1533            usage: hal_usage,
1534            memory_flags: hal::MemoryFlags::empty(),
1535            view_formats: hal_view_formats,
1536        };
1537
1538        let raw_texture = unsafe { self.raw().create_texture(&hal_desc) }
1539            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
1540
1541        let clear_mode = if hal_usage
1542            .intersects(wgt::TextureUses::DEPTH_STENCIL_WRITE | wgt::TextureUses::COLOR_TARGET)
1543            && desc.dimension == wgt::TextureDimension::D2
1544        {
1545            let (is_color, usage) = if desc.format.is_depth_stencil_format() {
1546                (false, wgt::TextureUses::DEPTH_STENCIL_WRITE)
1547            } else {
1548                (true, wgt::TextureUses::COLOR_TARGET)
1549            };
1550
1551            let clear_label = hal_label(
1552                Some("(wgpu internal) clear texture view"),
1553                self.instance_flags,
1554            );
1555
1556            let mut clear_views = SmallVec::new();
1557            for mip_level in 0..desc.mip_level_count {
1558                for array_layer in 0..desc.size.depth_or_array_layers {
1559                    macro_rules! push_clear_view {
1560                        ($format:expr, $aspect:expr) => {
1561                            let desc = hal::TextureViewDescriptor {
1562                                label: clear_label,
1563                                format: $format,
1564                                dimension: TextureViewDimension::D2,
1565                                usage,
1566                                range: wgt::ImageSubresourceRange {
1567                                    aspect: $aspect,
1568                                    base_mip_level: mip_level,
1569                                    mip_level_count: Some(1),
1570                                    base_array_layer: array_layer,
1571                                    array_layer_count: Some(1),
1572                                },
1573                            };
1574                            clear_views.push(ManuallyDrop::new(
1575                                unsafe {
1576                                    self.raw().create_texture_view(raw_texture.as_ref(), &desc)
1577                                }
1578                                .map_err(|e| self.handle_hal_error(e))?,
1579                            ));
1580                        };
1581                    }
1582
1583                    if let Some(planes) = desc.format.planes() {
1584                        for plane in 0..planes {
1585                            let aspect = wgt::TextureAspect::from_plane(plane).unwrap();
1586                            let format = desc.format.aspect_specific_format(aspect).unwrap();
1587                            push_clear_view!(format, aspect);
1588                        }
1589                    } else {
1590                        push_clear_view!(desc.format, wgt::TextureAspect::All);
1591                    }
1592                }
1593            }
1594            resource::TextureClearMode::RenderPass {
1595                clear_views,
1596                is_color,
1597            }
1598        } else {
1599            resource::TextureClearMode::BufferCopy
1600        };
1601
1602        let texture = Texture::new(
1603            self,
1604            resource::TextureInner::Native { raw: raw_texture },
1605            hal_usage,
1606            desc,
1607            format_features,
1608            clear_mode,
1609            true,
1610        );
1611
1612        let texture = Arc::new(texture);
1613
1614        self.trackers
1615            .lock()
1616            .textures
1617            .insert_single(&texture, wgt::TextureUses::UNINITIALIZED);
1618
1619        Ok(texture)
1620    }
1621
1622    pub fn create_texture_view(
1623        self: &Arc<Self>,
1624        texture: &Arc<Texture>,
1625        desc: &resource::TextureViewDescriptor,
1626    ) -> Result<Arc<TextureView>, resource::CreateTextureViewError> {
1627        self.check_is_valid()?;
1628
1629        let snatch_guard = texture.device.snatchable_lock.read();
1630
1631        let texture_raw = texture.try_raw(&snatch_guard)?;
1632
1633        // resolve TextureViewDescriptor defaults
1634        // https://gpuweb.github.io/gpuweb/#abstract-opdef-resolving-gputextureviewdescriptor-defaults
1635        let resolved_format = desc.format.unwrap_or_else(|| {
1636            texture
1637                .desc
1638                .format
1639                .aspect_specific_format(desc.range.aspect)
1640                .unwrap_or(texture.desc.format)
1641        });
1642
1643        let resolved_dimension = desc
1644            .dimension
1645            .unwrap_or_else(|| match texture.desc.dimension {
1646                wgt::TextureDimension::D1 => TextureViewDimension::D1,
1647                wgt::TextureDimension::D2 => {
1648                    if texture.desc.array_layer_count() == 1 {
1649                        TextureViewDimension::D2
1650                    } else {
1651                        TextureViewDimension::D2Array
1652                    }
1653                }
1654                wgt::TextureDimension::D3 => TextureViewDimension::D3,
1655            });
1656
1657        let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {
1658            texture
1659                .desc
1660                .mip_level_count
1661                .saturating_sub(desc.range.base_mip_level)
1662        });
1663
1664        let resolved_array_layer_count =
1665            desc.range
1666                .array_layer_count
1667                .unwrap_or_else(|| match resolved_dimension {
1668                    TextureViewDimension::D1
1669                    | TextureViewDimension::D2
1670                    | TextureViewDimension::D3 => 1,
1671                    TextureViewDimension::Cube => 6,
1672                    TextureViewDimension::D2Array | TextureViewDimension::CubeArray => texture
1673                        .desc
1674                        .array_layer_count()
1675                        .saturating_sub(desc.range.base_array_layer),
1676                });
1677
1678        let resolved_usage = {
1679            let usage = desc.usage.unwrap_or(wgt::TextureUsages::empty());
1680            if usage.is_empty() {
1681                texture.desc.usage
1682            } else if texture.desc.usage.contains(usage) {
1683                usage
1684            } else {
1685                return Err(resource::CreateTextureViewError::InvalidTextureViewUsage {
1686                    view: usage,
1687                    texture: texture.desc.usage,
1688                });
1689            }
1690        };
1691
1692        let format_features = self.describe_format_features(resolved_format)?;
1693        let allowed_format_usages = format_features.allowed_usages;
1694        if resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1695            && !allowed_format_usages.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1696        {
1697            return Err(
1698                resource::CreateTextureViewError::TextureViewFormatNotRenderable(resolved_format),
1699            );
1700        }
1701
1702        if resolved_usage.contains(wgt::TextureUsages::STORAGE_BINDING)
1703            && !allowed_format_usages.contains(wgt::TextureUsages::STORAGE_BINDING)
1704        {
1705            return Err(
1706                resource::CreateTextureViewError::TextureViewFormatNotStorage(resolved_format),
1707            );
1708        }
1709
1710        // validate TextureViewDescriptor
1711
1712        let aspects = hal::FormatAspects::new(texture.desc.format, desc.range.aspect);
1713        if aspects.is_empty() {
1714            return Err(resource::CreateTextureViewError::InvalidAspect {
1715                texture_format: texture.desc.format,
1716                requested_aspect: desc.range.aspect,
1717            });
1718        }
1719
1720        let format_is_good = if desc.range.aspect == wgt::TextureAspect::All {
1721            resolved_format == texture.desc.format
1722                || texture.desc.view_formats.contains(&resolved_format)
1723        } else {
1724            Some(resolved_format)
1725                == texture
1726                    .desc
1727                    .format
1728                    .aspect_specific_format(desc.range.aspect)
1729        };
1730        if !format_is_good {
1731            return Err(resource::CreateTextureViewError::FormatReinterpretation {
1732                texture: texture.desc.format,
1733                view: resolved_format,
1734            });
1735        }
1736
1737        // check if multisampled texture is seen as anything but 2D
1738        if texture.desc.sample_count > 1 && resolved_dimension != TextureViewDimension::D2 {
1739            return Err(
1740                resource::CreateTextureViewError::InvalidMultisampledTextureViewDimension(
1741                    resolved_dimension,
1742                ),
1743            );
1744        }
1745
1746        // check if the dimension is compatible with the texture
1747        if texture.desc.dimension != resolved_dimension.compatible_texture_dimension() {
1748            return Err(
1749                resource::CreateTextureViewError::InvalidTextureViewDimension {
1750                    view: resolved_dimension,
1751                    texture: texture.desc.dimension,
1752                },
1753            );
1754        }
1755
1756        match resolved_dimension {
1757            TextureViewDimension::D1 | TextureViewDimension::D2 | TextureViewDimension::D3 => {
1758                if resolved_array_layer_count != 1 {
1759                    return Err(resource::CreateTextureViewError::InvalidArrayLayerCount {
1760                        requested: resolved_array_layer_count,
1761                        dim: resolved_dimension,
1762                    });
1763                }
1764            }
1765            TextureViewDimension::Cube => {
1766                if resolved_array_layer_count != 6 {
1767                    return Err(
1768                        resource::CreateTextureViewError::InvalidCubemapTextureDepth {
1769                            depth: resolved_array_layer_count,
1770                        },
1771                    );
1772                }
1773            }
1774            TextureViewDimension::CubeArray => {
1775                if resolved_array_layer_count % 6 != 0 {
1776                    return Err(
1777                        resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth {
1778                            depth: resolved_array_layer_count,
1779                        },
1780                    );
1781                }
1782            }
1783            _ => {}
1784        }
1785
1786        match resolved_dimension {
1787            TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1788                if texture.desc.size.width != texture.desc.size.height {
1789                    return Err(resource::CreateTextureViewError::InvalidCubeTextureViewSize);
1790                }
1791            }
1792            _ => {}
1793        }
1794
1795        if resolved_mip_level_count == 0 {
1796            return Err(resource::CreateTextureViewError::ZeroMipLevelCount);
1797        }
1798
1799        let mip_level_end = desc
1800            .range
1801            .base_mip_level
1802            .saturating_add(resolved_mip_level_count);
1803
1804        let level_end = texture.desc.mip_level_count;
1805        if mip_level_end > level_end {
1806            return Err(resource::CreateTextureViewError::TooManyMipLevels {
1807                base_mip_level: desc.range.base_mip_level,
1808                mip_level_count: resolved_mip_level_count,
1809                total: level_end,
1810            });
1811        }
1812
1813        if resolved_array_layer_count == 0 {
1814            return Err(resource::CreateTextureViewError::ZeroArrayLayerCount);
1815        }
1816
1817        let array_layer_end = desc
1818            .range
1819            .base_array_layer
1820            .saturating_add(resolved_array_layer_count);
1821
1822        let layer_end = texture.desc.array_layer_count();
1823        if array_layer_end > layer_end {
1824            return Err(resource::CreateTextureViewError::TooManyArrayLayers {
1825                base_array_layer: desc.range.base_array_layer,
1826                array_layer_count: resolved_array_layer_count,
1827                total: layer_end,
1828            });
1829        };
1830
1831        // https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view
1832        let render_extent = 'error: {
1833            if !resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1834                break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage));
1835            }
1836
1837            let allowed_view_dimensions = [
1838                TextureViewDimension::D2,
1839                TextureViewDimension::D2Array,
1840                TextureViewDimension::D3,
1841            ];
1842            if !allowed_view_dimensions.contains(&resolved_dimension) {
1843                break 'error Err(TextureViewNotRenderableReason::Dimension(
1844                    resolved_dimension,
1845                ));
1846            }
1847
1848            if resolved_mip_level_count != 1 {
1849                break 'error Err(TextureViewNotRenderableReason::MipLevelCount(
1850                    resolved_mip_level_count,
1851                ));
1852            }
1853
1854            if resolved_array_layer_count != 1
1855                && !(self.features.contains(wgt::Features::MULTIVIEW))
1856            {
1857                break 'error Err(TextureViewNotRenderableReason::ArrayLayerCount(
1858                    resolved_array_layer_count,
1859                ));
1860            }
1861
1862            if !texture.desc.format.is_multi_planar_format()
1863                && aspects != hal::FormatAspects::from(texture.desc.format)
1864            {
1865                break 'error Err(TextureViewNotRenderableReason::Aspects(aspects));
1866            }
1867
1868            Ok(texture
1869                .desc
1870                .compute_render_extent(desc.range.base_mip_level, desc.range.aspect.to_plane()))
1871        };
1872
1873        // filter the usages based on the other criteria
1874        let usage = {
1875            let resolved_hal_usage = conv::map_texture_usage(
1876                resolved_usage,
1877                resolved_format.into(),
1878                format_features.flags,
1879            );
1880            let mask_copy = !(wgt::TextureUses::COPY_SRC | wgt::TextureUses::COPY_DST);
1881            let mask_dimension = match resolved_dimension {
1882                TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1883                    wgt::TextureUses::RESOURCE
1884                }
1885                TextureViewDimension::D3 => {
1886                    wgt::TextureUses::RESOURCE
1887                        | wgt::TextureUses::STORAGE_READ_ONLY
1888                        | wgt::TextureUses::STORAGE_WRITE_ONLY
1889                        | wgt::TextureUses::STORAGE_READ_WRITE
1890                }
1891                _ => wgt::TextureUses::all(),
1892            };
1893            let mask_mip_level = if resolved_mip_level_count == 1 {
1894                wgt::TextureUses::all()
1895            } else {
1896                wgt::TextureUses::RESOURCE
1897            };
1898            resolved_hal_usage & mask_copy & mask_dimension & mask_mip_level
1899        };
1900
1901        // use the combined depth-stencil format for the view
1902        let format = if resolved_format.is_depth_stencil_component(texture.desc.format) {
1903            texture.desc.format
1904        } else {
1905            resolved_format
1906        };
1907
1908        let resolved_range = wgt::ImageSubresourceRange {
1909            aspect: desc.range.aspect,
1910            base_mip_level: desc.range.base_mip_level,
1911            mip_level_count: Some(resolved_mip_level_count),
1912            base_array_layer: desc.range.base_array_layer,
1913            array_layer_count: Some(resolved_array_layer_count),
1914        };
1915
1916        let hal_desc = hal::TextureViewDescriptor {
1917            label: desc.label.to_hal(self.instance_flags),
1918            format,
1919            dimension: resolved_dimension,
1920            usage,
1921            range: resolved_range,
1922        };
1923
1924        let raw = unsafe { self.raw().create_texture_view(texture_raw, &hal_desc) }
1925            .map_err(|e| self.handle_hal_error(e))?;
1926
1927        let selector = TextureSelector {
1928            mips: desc.range.base_mip_level..mip_level_end,
1929            layers: desc.range.base_array_layer..array_layer_end,
1930        };
1931
1932        let view = TextureView {
1933            raw: Snatchable::new(raw),
1934            parent: texture.clone(),
1935            device: self.clone(),
1936            desc: resource::HalTextureViewDescriptor {
1937                texture_format: texture.desc.format,
1938                format: resolved_format,
1939                dimension: resolved_dimension,
1940                usage: resolved_usage,
1941                range: resolved_range,
1942            },
1943            format_features: texture.format_features,
1944            render_extent,
1945            samples: texture.desc.sample_count,
1946            selector,
1947            label: desc.label.to_string(),
1948        };
1949
1950        let view = Arc::new(view);
1951
1952        {
1953            let mut views = texture.views.lock();
1954            views.push(Arc::downgrade(&view));
1955        }
1956
1957        Ok(view)
1958    }
1959
1960    pub fn create_external_texture(
1961        self: &Arc<Self>,
1962        desc: &resource::ExternalTextureDescriptor,
1963        planes: &[Arc<TextureView>],
1964    ) -> Result<Arc<ExternalTexture>, resource::CreateExternalTextureError> {
1965        use resource::CreateExternalTextureError;
1966        self.require_features(wgt::Features::EXTERNAL_TEXTURE)?;
1967        self.check_is_valid()?;
1968
1969        if desc.num_planes() != planes.len() {
1970            return Err(CreateExternalTextureError::IncorrectPlaneCount {
1971                format: desc.format,
1972                expected: desc.num_planes(),
1973                provided: planes.len(),
1974            });
1975        }
1976
1977        let planes = planes
1978            .iter()
1979            .enumerate()
1980            .map(|(i, plane)| {
1981                if plane.samples != 1 {
1982                    return Err(CreateExternalTextureError::InvalidPlaneMultisample(
1983                        plane.samples,
1984                    ));
1985                }
1986
1987                let sample_type = plane
1988                    .desc
1989                    .format
1990                    .sample_type(Some(plane.desc.range.aspect), Some(self.features))
1991                    .unwrap();
1992                if !matches!(sample_type, TextureSampleType::Float { filterable: true }) {
1993                    return Err(CreateExternalTextureError::InvalidPlaneSampleType {
1994                        format: plane.desc.format,
1995                        sample_type,
1996                    });
1997                }
1998
1999                if plane.desc.dimension != TextureViewDimension::D2 {
2000                    return Err(CreateExternalTextureError::InvalidPlaneDimension(
2001                        plane.desc.dimension,
2002                    ));
2003                }
2004
2005                let expected_components = match desc.format {
2006                    wgt::ExternalTextureFormat::Rgba => 4,
2007                    wgt::ExternalTextureFormat::Nv12 => match i {
2008                        0 => 1,
2009                        1 => 2,
2010                        _ => unreachable!(),
2011                    },
2012                    wgt::ExternalTextureFormat::Yu12 => 1,
2013                };
2014                if plane.desc.format.components() != expected_components {
2015                    return Err(CreateExternalTextureError::InvalidPlaneFormat {
2016                        format: desc.format,
2017                        plane: i,
2018                        expected: expected_components,
2019                        provided: plane.desc.format,
2020                    });
2021                }
2022
2023                plane.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
2024                Ok(plane.clone())
2025            })
2026            .collect::<Result<_, _>>()?;
2027
2028        let params_data = ExternalTextureParams::from_desc(desc);
2029        let label = desc.label.as_ref().map(|l| alloc::format!("{l} params"));
2030        let params_desc = resource::BufferDescriptor {
2031            label: label.map(Cow::Owned),
2032            size: size_of_val(&params_data) as wgt::BufferAddress,
2033            usage: wgt::BufferUsages::UNIFORM | wgt::BufferUsages::COPY_DST,
2034            mapped_at_creation: false,
2035        };
2036        let params = self.create_buffer(&params_desc)?;
2037        self.get_queue().unwrap().write_buffer(
2038            params.clone(),
2039            0,
2040            bytemuck::bytes_of(&params_data),
2041        )?;
2042
2043        let external_texture = ExternalTexture {
2044            device: self.clone(),
2045            planes,
2046            params,
2047            label: desc.label.to_string(),
2048            tracking_data: TrackingData::new(self.tracker_indices.external_textures.clone()),
2049        };
2050        let external_texture = Arc::new(external_texture);
2051
2052        Ok(external_texture)
2053    }
2054
2055    pub fn create_sampler(
2056        self: &Arc<Self>,
2057        desc: &resource::SamplerDescriptor,
2058    ) -> Result<Arc<Sampler>, resource::CreateSamplerError> {
2059        self.check_is_valid()?;
2060
2061        if desc
2062            .address_modes
2063            .iter()
2064            .any(|am| am == &wgt::AddressMode::ClampToBorder)
2065        {
2066            self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER)?;
2067        }
2068
2069        if desc.border_color == Some(wgt::SamplerBorderColor::Zero) {
2070            self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;
2071        }
2072
2073        if desc.lod_min_clamp < 0.0 {
2074            return Err(resource::CreateSamplerError::InvalidLodMinClamp(
2075                desc.lod_min_clamp,
2076            ));
2077        }
2078        if desc.lod_max_clamp < desc.lod_min_clamp {
2079            return Err(resource::CreateSamplerError::InvalidLodMaxClamp {
2080                lod_min_clamp: desc.lod_min_clamp,
2081                lod_max_clamp: desc.lod_max_clamp,
2082            });
2083        }
2084
2085        if desc.anisotropy_clamp < 1 {
2086            return Err(resource::CreateSamplerError::InvalidAnisotropy(
2087                desc.anisotropy_clamp,
2088            ));
2089        }
2090
2091        if desc.anisotropy_clamp != 1 {
2092            if !matches!(desc.min_filter, wgt::FilterMode::Linear) {
2093                return Err(
2094                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
2095                        filter_type: resource::SamplerFilterErrorType::MinFilter,
2096                        filter_mode: desc.min_filter,
2097                        anisotropic_clamp: desc.anisotropy_clamp,
2098                    },
2099                );
2100            }
2101            if !matches!(desc.mag_filter, wgt::FilterMode::Linear) {
2102                return Err(
2103                    resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
2104                        filter_type: resource::SamplerFilterErrorType::MagFilter,
2105                        filter_mode: desc.mag_filter,
2106                        anisotropic_clamp: desc.anisotropy_clamp,
2107                    },
2108                );
2109            }
2110            if !matches!(desc.mipmap_filter, wgt::MipmapFilterMode::Linear) {
2111                return Err(
2112                    resource::CreateSamplerError::InvalidMipmapFilterModeWithAnisotropy {
2113                        filter_type: resource::SamplerFilterErrorType::MipmapFilter,
2114                        filter_mode: desc.mipmap_filter,
2115                        anisotropic_clamp: desc.anisotropy_clamp,
2116                    },
2117                );
2118            }
2119        }
2120
2121        let anisotropy_clamp = if self
2122            .downlevel
2123            .flags
2124            .contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING)
2125        {
2126            // Clamp anisotropy clamp to [1, 16] per the wgpu-hal interface
2127            desc.anisotropy_clamp.min(16)
2128        } else {
2129            // If it isn't supported, set this unconditionally to 1
2130            1
2131        };
2132
2133        //TODO: check for wgt::DownlevelFlags::COMPARISON_SAMPLERS
2134
2135        let hal_desc = hal::SamplerDescriptor {
2136            label: desc.label.to_hal(self.instance_flags),
2137            address_modes: desc.address_modes,
2138            mag_filter: desc.mag_filter,
2139            min_filter: desc.min_filter,
2140            mipmap_filter: desc.mipmap_filter,
2141            lod_clamp: desc.lod_min_clamp..desc.lod_max_clamp,
2142            compare: desc.compare,
2143            anisotropy_clamp,
2144            border_color: desc.border_color,
2145        };
2146
2147        let raw = unsafe { self.raw().create_sampler(&hal_desc) }
2148            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
2149
2150        let sampler = Sampler {
2151            raw: ManuallyDrop::new(raw),
2152            device: self.clone(),
2153            label: desc.label.to_string(),
2154            tracking_data: TrackingData::new(self.tracker_indices.samplers.clone()),
2155            comparison: desc.compare.is_some(),
2156            filtering: desc.min_filter == wgt::FilterMode::Linear
2157                || desc.mag_filter == wgt::FilterMode::Linear
2158                || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,
2159        };
2160
2161        let sampler = Arc::new(sampler);
2162
2163        Ok(sampler)
2164    }
2165
2166    pub fn create_shader_module<'a>(
2167        self: &Arc<Self>,
2168        desc: &pipeline::ShaderModuleDescriptor<'a>,
2169        source: pipeline::ShaderModuleSource<'a>,
2170    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
2171        self.check_is_valid()?;
2172
2173        let (module, source) = match source {
2174            #[cfg(feature = "wgsl")]
2175            pipeline::ShaderModuleSource::Wgsl(code) => {
2176                profiling::scope!("naga::front::wgsl::parse_str");
2177                let module = naga::front::wgsl::parse_str(&code).map_err(|inner| {
2178                    pipeline::CreateShaderModuleError::Parsing(naga::error::ShaderError {
2179                        source: code.to_string(),
2180                        label: desc.label.as_ref().map(|l| l.to_string()),
2181                        inner: Box::new(inner),
2182                    })
2183                })?;
2184                (Cow::Owned(module), code.into_owned())
2185            }
2186            #[cfg(feature = "spirv")]
2187            pipeline::ShaderModuleSource::SpirV(spv, options) => {
2188                let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options);
2189                profiling::scope!("naga::front::spv::Frontend");
2190                let module = parser.parse().map_err(|inner| {
2191                    pipeline::CreateShaderModuleError::ParsingSpirV(naga::error::ShaderError {
2192                        source: String::new(),
2193                        label: desc.label.as_ref().map(|l| l.to_string()),
2194                        inner: Box::new(inner),
2195                    })
2196                })?;
2197                (Cow::Owned(module), String::new())
2198            }
2199            #[cfg(feature = "glsl")]
2200            pipeline::ShaderModuleSource::Glsl(code, options) => {
2201                let mut parser = naga::front::glsl::Frontend::default();
2202                profiling::scope!("naga::front::glsl::Frontend.parse");
2203                let module = parser.parse(&options, &code).map_err(|inner| {
2204                    pipeline::CreateShaderModuleError::ParsingGlsl(naga::error::ShaderError {
2205                        source: code.to_string(),
2206                        label: desc.label.as_ref().map(|l| l.to_string()),
2207                        inner: Box::new(inner),
2208                    })
2209                })?;
2210                (Cow::Owned(module), code.into_owned())
2211            }
2212            pipeline::ShaderModuleSource::Naga(module) => (module, String::new()),
2213            pipeline::ShaderModuleSource::Dummy(_) => panic!("found `ShaderModuleSource::Dummy`"),
2214        };
2215        for (_, var) in module.global_variables.iter() {
2216            match var.binding {
2217                Some(br) if br.group >= self.limits.max_bind_groups => {
2218                    return Err(pipeline::CreateShaderModuleError::InvalidGroupIndex {
2219                        bind: br,
2220                        group: br.group,
2221                        limit: self.limits.max_bind_groups,
2222                    });
2223                }
2224                _ => continue,
2225            };
2226        }
2227
2228        profiling::scope!("naga::validate");
2229        let debug_source =
2230            if self.instance_flags.contains(wgt::InstanceFlags::DEBUG) && !source.is_empty() {
2231                Some(hal::DebugSource {
2232                    file_name: Cow::Owned(
2233                        desc.label
2234                            .as_ref()
2235                            .map_or("shader".to_string(), |l| l.to_string()),
2236                    ),
2237                    source_code: Cow::Owned(source.clone()),
2238                })
2239            } else {
2240                None
2241            };
2242
2243        let info = create_validator(
2244            self.features,
2245            self.downlevel.flags,
2246            naga::valid::ValidationFlags::all(),
2247        )
2248        .validate(&module)
2249        .map_err(|inner| {
2250            pipeline::CreateShaderModuleError::Validation(naga::error::ShaderError {
2251                source,
2252                label: desc.label.as_ref().map(|l| l.to_string()),
2253                inner: Box::new(inner),
2254            })
2255        })?;
2256
2257        let interface = validation::Interface::new(&module, &info, self.limits.clone());
2258        let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {
2259            module,
2260            info,
2261            debug_source,
2262        });
2263        let hal_desc = hal::ShaderModuleDescriptor {
2264            label: desc.label.to_hal(self.instance_flags),
2265            runtime_checks: desc.runtime_checks,
2266        };
2267        let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {
2268            Ok(raw) => raw,
2269            Err(error) => {
2270                return Err(match error {
2271                    hal::ShaderError::Device(error) => {
2272                        pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
2273                    }
2274                    hal::ShaderError::Compilation(ref msg) => {
2275                        log::error!("Shader error: {msg}");
2276                        pipeline::CreateShaderModuleError::Generation
2277                    }
2278                })
2279            }
2280        };
2281
2282        let module = pipeline::ShaderModule {
2283            raw: ManuallyDrop::new(raw),
2284            device: self.clone(),
2285            interface: Some(interface),
2286            label: desc.label.to_string(),
2287        };
2288
2289        let module = Arc::new(module);
2290
2291        Ok(module)
2292    }
2293
2294    /// Not a public API. For use by `player` only.
2295    #[allow(unused_unsafe)]
2296    #[doc(hidden)]
2297    pub unsafe fn create_shader_module_passthrough<'a>(
2298        self: &Arc<Self>,
2299        descriptor: &pipeline::ShaderModuleDescriptorPassthrough<'a>,
2300    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
2301        self.check_is_valid()?;
2302        self.require_features(wgt::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS)?;
2303
2304        // TODO: when we get to use if-let chains, this will be a little nicer!
2305
2306        log::info!("Backend: {}", self.backend());
2307        let hal_shader = match self.backend() {
2308            wgt::Backend::Vulkan => hal::ShaderInput::SpirV(
2309                descriptor
2310                    .spirv
2311                    .as_ref()
2312                    .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,
2313            ),
2314            wgt::Backend::Dx12 => {
2315                if let Some(dxil) = &descriptor.dxil {
2316                    hal::ShaderInput::Dxil {
2317                        shader: dxil,
2318                        entry_point: descriptor.entry_point.clone(),
2319                        num_workgroups: descriptor.num_workgroups,
2320                    }
2321                } else if let Some(hlsl) = &descriptor.hlsl {
2322                    hal::ShaderInput::Hlsl {
2323                        shader: hlsl,
2324                        entry_point: descriptor.entry_point.clone(),
2325                        num_workgroups: descriptor.num_workgroups,
2326                    }
2327                } else {
2328                    return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend);
2329                }
2330            }
2331            wgt::Backend::Metal => hal::ShaderInput::Msl {
2332                shader: descriptor
2333                    .msl
2334                    .as_ref()
2335                    .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,
2336                entry_point: descriptor.entry_point.clone(),
2337                num_workgroups: descriptor.num_workgroups,
2338            },
2339            wgt::Backend::Gl => hal::ShaderInput::Glsl {
2340                shader: descriptor
2341                    .glsl
2342                    .as_ref()
2343                    .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,
2344                entry_point: descriptor.entry_point.clone(),
2345                num_workgroups: descriptor.num_workgroups,
2346            },
2347            wgt::Backend::Noop => {
2348                return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend)
2349            }
2350            wgt::Backend::BrowserWebGpu => unreachable!(),
2351        };
2352
2353        let hal_desc = hal::ShaderModuleDescriptor {
2354            label: descriptor.label.to_hal(self.instance_flags),
2355            runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),
2356        };
2357
2358        let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {
2359            Ok(raw) => raw,
2360            Err(error) => {
2361                return Err(match error {
2362                    hal::ShaderError::Device(error) => {
2363                        pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
2364                    }
2365                    hal::ShaderError::Compilation(ref msg) => {
2366                        log::error!("Shader error: {msg}");
2367                        pipeline::CreateShaderModuleError::Generation
2368                    }
2369                })
2370            }
2371        };
2372
2373        let module = pipeline::ShaderModule {
2374            raw: ManuallyDrop::new(raw),
2375            device: self.clone(),
2376            interface: None,
2377            label: descriptor.label.to_string(),
2378        };
2379
2380        Ok(Arc::new(module))
2381    }
2382
2383    pub(crate) fn create_command_encoder(
2384        self: &Arc<Self>,
2385        label: &crate::Label,
2386    ) -> Result<Arc<command::CommandEncoder>, DeviceError> {
2387        self.check_is_valid()?;
2388
2389        let queue = self.get_queue().unwrap();
2390
2391        let encoder = self
2392            .command_allocator
2393            .acquire_encoder(self.raw(), queue.raw())
2394            .map_err(|e| self.handle_hal_error(e))?;
2395
2396        let cmd_enc = command::CommandEncoder::new(encoder, self, label);
2397
2398        let cmd_enc = Arc::new(cmd_enc);
2399
2400        Ok(cmd_enc)
2401    }
2402
2403    /// Generate information about late-validated buffer bindings for pipelines.
2404    //TODO: should this be combined with `get_introspection_bind_group_layouts` in some way?
2405    fn make_late_sized_buffer_groups(
2406        shader_binding_sizes: &FastHashMap<naga::ResourceBinding, wgt::BufferSize>,
2407        layout: &binding_model::PipelineLayout,
2408    ) -> ArrayVec<pipeline::LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }> {
2409        // Given the shader-required binding sizes and the pipeline layout,
2410        // return the filtered list of them in the layout order,
2411        // removing those with given `min_binding_size`.
2412        layout
2413            .bind_group_layouts
2414            .iter()
2415            .enumerate()
2416            .map(|(group_index, bgl)| pipeline::LateSizedBufferGroup {
2417                shader_sizes: bgl
2418                    .entries
2419                    .values()
2420                    .filter_map(|entry| match entry.ty {
2421                        wgt::BindingType::Buffer {
2422                            min_binding_size: None,
2423                            ..
2424                        } => {
2425                            let rb = naga::ResourceBinding {
2426                                group: group_index as u32,
2427                                binding: entry.binding,
2428                            };
2429                            let shader_size =
2430                                shader_binding_sizes.get(&rb).map_or(0, |nz| nz.get());
2431                            Some(shader_size)
2432                        }
2433                        _ => None,
2434                    })
2435                    .collect(),
2436            })
2437            .collect()
2438    }
2439
2440    pub fn create_bind_group_layout(
2441        self: &Arc<Self>,
2442        desc: &binding_model::BindGroupLayoutDescriptor,
2443    ) -> Result<Arc<BindGroupLayout>, binding_model::CreateBindGroupLayoutError> {
2444        self.check_is_valid()?;
2445
2446        let entry_map = bgl::EntryMap::from_entries(&desc.entries)?;
2447
2448        let bgl_result = self.bgl_pool.get_or_init(entry_map, |entry_map| {
2449            let bgl =
2450                self.create_bind_group_layout_internal(&desc.label, entry_map, bgl::Origin::Pool)?;
2451            bgl.exclusive_pipeline
2452                .set(binding_model::ExclusivePipeline::None)
2453                .unwrap();
2454            Ok(bgl)
2455        });
2456
2457        match bgl_result {
2458            Ok(layout) => Ok(layout),
2459            Err(e) => Err(e),
2460        }
2461    }
2462
2463    /// Internal function exposed for use by `player` crate only.
2464    #[doc(hidden)]
2465    pub fn create_bind_group_layout_internal(
2466        self: &Arc<Self>,
2467        label: &crate::Label,
2468        entry_map: bgl::EntryMap,
2469        origin: bgl::Origin,
2470    ) -> Result<Arc<BindGroupLayout>, binding_model::CreateBindGroupLayoutError> {
2471        #[derive(PartialEq)]
2472        enum WritableStorage {
2473            Yes,
2474            No,
2475        }
2476
2477        for entry in entry_map.values() {
2478            if entry.binding >= self.limits.max_bindings_per_bind_group {
2479                return Err(
2480                    binding_model::CreateBindGroupLayoutError::InvalidBindingIndex {
2481                        binding: entry.binding,
2482                        maximum: self.limits.max_bindings_per_bind_group,
2483                    },
2484                );
2485            }
2486
2487            use wgt::BindingType as Bt;
2488
2489            let mut required_features = wgt::Features::empty();
2490            let mut required_downlevel_flags = wgt::DownlevelFlags::empty();
2491            let (array_feature, writable_storage) = match entry.ty {
2492                Bt::Buffer {
2493                    ty: wgt::BufferBindingType::Uniform,
2494                    has_dynamic_offset: false,
2495                    min_binding_size: _,
2496                } => (
2497                    Some(wgt::Features::BUFFER_BINDING_ARRAY),
2498                    WritableStorage::No,
2499                ),
2500                Bt::Buffer {
2501                    ty: wgt::BufferBindingType::Uniform,
2502                    has_dynamic_offset: true,
2503                    min_binding_size: _,
2504                } => (
2505                    Some(wgt::Features::BUFFER_BINDING_ARRAY),
2506                    WritableStorage::No,
2507                ),
2508                Bt::Buffer {
2509                    ty: wgt::BufferBindingType::Storage { read_only },
2510                    ..
2511                } => (
2512                    Some(
2513                        wgt::Features::BUFFER_BINDING_ARRAY
2514                            | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
2515                    ),
2516                    match read_only {
2517                        true => WritableStorage::No,
2518                        false => WritableStorage::Yes,
2519                    },
2520                ),
2521                Bt::Sampler { .. } => (
2522                    Some(wgt::Features::TEXTURE_BINDING_ARRAY),
2523                    WritableStorage::No,
2524                ),
2525                Bt::Texture {
2526                    multisampled: true,
2527                    sample_type: TextureSampleType::Float { filterable: true },
2528                    ..
2529                } => {
2530                    return Err(binding_model::CreateBindGroupLayoutError::Entry {
2531                        binding: entry.binding,
2532                        error:
2533                            BindGroupLayoutEntryError::SampleTypeFloatFilterableBindingMultisampled,
2534                    });
2535                }
2536                Bt::Texture {
2537                    multisampled,
2538                    view_dimension,
2539                    ..
2540                } => {
2541                    if multisampled && view_dimension != TextureViewDimension::D2 {
2542                        return Err(binding_model::CreateBindGroupLayoutError::Entry {
2543                            binding: entry.binding,
2544                            error: BindGroupLayoutEntryError::Non2DMultisampled(view_dimension),
2545                        });
2546                    }
2547
2548                    (
2549                        Some(wgt::Features::TEXTURE_BINDING_ARRAY),
2550                        WritableStorage::No,
2551                    )
2552                }
2553                Bt::StorageTexture {
2554                    access,
2555                    view_dimension,
2556                    format: _,
2557                } => {
2558                    match view_dimension {
2559                        TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
2560                            return Err(binding_model::CreateBindGroupLayoutError::Entry {
2561                                binding: entry.binding,
2562                                error: BindGroupLayoutEntryError::StorageTextureCube,
2563                            })
2564                        }
2565                        _ => (),
2566                    }
2567                    match access {
2568                        wgt::StorageTextureAccess::Atomic
2569                            if !self.features.contains(wgt::Features::TEXTURE_ATOMIC) =>
2570                        {
2571                            return Err(binding_model::CreateBindGroupLayoutError::Entry {
2572                                binding: entry.binding,
2573                                error: BindGroupLayoutEntryError::StorageTextureAtomic,
2574                            });
2575                        }
2576                        _ => (),
2577                    }
2578                    (
2579                        Some(
2580                            wgt::Features::TEXTURE_BINDING_ARRAY
2581                                | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
2582                        ),
2583                        match access {
2584                            wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes,
2585                            wgt::StorageTextureAccess::ReadOnly => WritableStorage::No,
2586                            wgt::StorageTextureAccess::ReadWrite => WritableStorage::Yes,
2587                            wgt::StorageTextureAccess::Atomic => {
2588                                required_features |= wgt::Features::TEXTURE_ATOMIC;
2589                                WritableStorage::Yes
2590                            }
2591                        },
2592                    )
2593                }
2594                Bt::AccelerationStructure { vertex_return } => {
2595                    self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)
2596                        .map_err(|e| binding_model::CreateBindGroupLayoutError::Entry {
2597                            binding: entry.binding,
2598                            error: e.into(),
2599                        })?;
2600                    if vertex_return {
2601                        self.require_features(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN)
2602                            .map_err(|e| binding_model::CreateBindGroupLayoutError::Entry {
2603                                binding: entry.binding,
2604                                error: e.into(),
2605                            })?;
2606                    }
2607
2608                    (None, WritableStorage::No)
2609                }
2610                Bt::ExternalTexture => {
2611                    self.require_features(wgt::Features::EXTERNAL_TEXTURE)
2612                        .map_err(|e| binding_model::CreateBindGroupLayoutError::Entry {
2613                            binding: entry.binding,
2614                            error: e.into(),
2615                        })?;
2616                    (None, WritableStorage::No)
2617                }
2618            };
2619
2620            // Validate the count parameter
2621            if entry.count.is_some() {
2622                required_features |= array_feature
2623                    .ok_or(BindGroupLayoutEntryError::ArrayUnsupported)
2624                    .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
2625                        binding: entry.binding,
2626                        error,
2627                    })?;
2628            }
2629
2630            if entry.visibility.contains_unknown_bits() {
2631                return Err(
2632                    binding_model::CreateBindGroupLayoutError::InvalidVisibility(entry.visibility),
2633                );
2634            }
2635
2636            if entry.visibility.contains(wgt::ShaderStages::VERTEX) {
2637                if writable_storage == WritableStorage::Yes {
2638                    required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
2639                }
2640                if let Bt::Buffer {
2641                    ty: wgt::BufferBindingType::Storage { .. },
2642                    ..
2643                } = entry.ty
2644                {
2645                    required_downlevel_flags |= wgt::DownlevelFlags::VERTEX_STORAGE;
2646                }
2647            }
2648            if writable_storage == WritableStorage::Yes
2649                && entry.visibility.contains(wgt::ShaderStages::FRAGMENT)
2650            {
2651                required_downlevel_flags |= wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE;
2652            }
2653
2654            self.require_features(required_features)
2655                .map_err(BindGroupLayoutEntryError::MissingFeatures)
2656                .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
2657                    binding: entry.binding,
2658                    error,
2659                })?;
2660            self.require_downlevel_flags(required_downlevel_flags)
2661                .map_err(BindGroupLayoutEntryError::MissingDownlevelFlags)
2662                .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
2663                    binding: entry.binding,
2664                    error,
2665                })?;
2666        }
2667
2668        let bgl_flags = conv::bind_group_layout_flags(self.features);
2669
2670        let hal_bindings = entry_map.values().copied().collect::<Vec<_>>();
2671        let hal_desc = hal::BindGroupLayoutDescriptor {
2672            label: label.to_hal(self.instance_flags),
2673            flags: bgl_flags,
2674            entries: &hal_bindings,
2675        };
2676
2677        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
2678        for entry in entry_map.values() {
2679            count_validator.add_binding(entry);
2680        }
2681        // If a single bind group layout violates limits, the pipeline layout is
2682        // definitely going to violate limits too, lets catch it now.
2683        count_validator
2684            .validate(&self.limits)
2685            .map_err(binding_model::CreateBindGroupLayoutError::TooManyBindings)?;
2686
2687        // Validate that binding arrays don't conflict with dynamic offsets.
2688        count_validator.validate_binding_arrays()?;
2689
2690        let raw = unsafe { self.raw().create_bind_group_layout(&hal_desc) }
2691            .map_err(|e| self.handle_hal_error(e))?;
2692
2693        let bgl = BindGroupLayout {
2694            raw: ManuallyDrop::new(raw),
2695            device: self.clone(),
2696            entries: entry_map,
2697            origin,
2698            exclusive_pipeline: OnceCellOrLock::new(),
2699            binding_count_validator: count_validator,
2700            label: label.to_string(),
2701        };
2702
2703        let bgl = Arc::new(bgl);
2704
2705        Ok(bgl)
2706    }
2707
2708    fn create_buffer_binding<'a>(
2709        &self,
2710        bb: &'a binding_model::ResolvedBufferBinding,
2711        binding: u32,
2712        decl: &wgt::BindGroupLayoutEntry,
2713        used_buffer_ranges: &mut Vec<BufferInitTrackerAction>,
2714        dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
2715        late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
2716        used: &mut BindGroupStates,
2717        snatch_guard: &'a SnatchGuard<'a>,
2718    ) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, binding_model::CreateBindGroupError>
2719    {
2720        use crate::binding_model::CreateBindGroupError as Error;
2721
2722        let (binding_ty, dynamic, min_size) = match decl.ty {
2723            wgt::BindingType::Buffer {
2724                ty,
2725                has_dynamic_offset,
2726                min_binding_size,
2727            } => (ty, has_dynamic_offset, min_binding_size),
2728            _ => {
2729                return Err(Error::WrongBindingType {
2730                    binding,
2731                    actual: decl.ty,
2732                    expected: "UniformBuffer, StorageBuffer or ReadonlyStorageBuffer",
2733                })
2734            }
2735        };
2736
2737        let (pub_usage, internal_use, range_limit) = match binding_ty {
2738            wgt::BufferBindingType::Uniform => (
2739                wgt::BufferUsages::UNIFORM,
2740                wgt::BufferUses::UNIFORM,
2741                self.limits.max_uniform_buffer_binding_size,
2742            ),
2743            wgt::BufferBindingType::Storage { read_only } => (
2744                wgt::BufferUsages::STORAGE,
2745                if read_only {
2746                    wgt::BufferUses::STORAGE_READ_ONLY
2747                } else {
2748                    wgt::BufferUses::STORAGE_READ_WRITE
2749                },
2750                self.limits.max_storage_buffer_binding_size,
2751            ),
2752        };
2753
2754        let (align, align_limit_name) =
2755            binding_model::buffer_binding_type_alignment(&self.limits, binding_ty);
2756        if bb.offset % align as u64 != 0 {
2757            return Err(Error::UnalignedBufferOffset(
2758                bb.offset,
2759                align_limit_name,
2760                align,
2761            ));
2762        }
2763
2764        let buffer = &bb.buffer;
2765
2766        used.buffers.insert_single(buffer.clone(), internal_use);
2767
2768        buffer.same_device(self)?;
2769
2770        buffer.check_usage(pub_usage)?;
2771
2772        let (bb, bind_size) = buffer.binding(bb.offset, bb.size, snatch_guard)?;
2773
2774        if matches!(binding_ty, wgt::BufferBindingType::Storage { .. }) {
2775            let storage_buf_size_alignment = 4;
2776
2777            let aligned = bind_size % u64::from(storage_buf_size_alignment) == 0;
2778            if !aligned {
2779                return Err(Error::UnalignedEffectiveBufferBindingSizeForStorage {
2780                    alignment: storage_buf_size_alignment,
2781                    size: bind_size,
2782                });
2783            }
2784        }
2785
2786        let bind_end = bb.offset + bind_size;
2787
2788        if bind_size > range_limit as u64 {
2789            return Err(Error::BufferRangeTooLarge {
2790                binding,
2791                given: bind_size as u32,
2792                limit: range_limit,
2793            });
2794        }
2795
2796        // Record binding info for validating dynamic offsets
2797        if dynamic {
2798            dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
2799                binding_idx: binding,
2800                buffer_size: buffer.size,
2801                binding_range: bb.offset..bind_end,
2802                maximum_dynamic_offset: buffer.size - bind_end,
2803                binding_type: binding_ty,
2804            });
2805        }
2806
2807        if let Some(non_zero) = min_size {
2808            let min_size = non_zero.get();
2809            if min_size > bind_size {
2810                return Err(Error::BindingSizeTooSmall {
2811                    buffer: buffer.error_ident(),
2812                    actual: bind_size,
2813                    min: min_size,
2814                });
2815            }
2816        } else {
2817            let late_size = wgt::BufferSize::new(bind_size)
2818                .ok_or_else(|| Error::BindingZeroSize(buffer.error_ident()))?;
2819            late_buffer_binding_sizes.insert(binding, late_size);
2820        }
2821
2822        // This was checked against the device's alignment requirements above,
2823        // which should always be a multiple of `COPY_BUFFER_ALIGNMENT`.
2824        assert_eq!(bb.offset % wgt::COPY_BUFFER_ALIGNMENT, 0);
2825
2826        // `wgpu_hal` only restricts shader access to bound buffer regions with
2827        // a certain resolution. For the sake of lazy initialization, round up
2828        // the size of the bound range to reflect how much of the buffer is
2829        // actually going to be visible to the shader.
2830        let bounds_check_alignment =
2831            binding_model::buffer_binding_type_bounds_check_alignment(&self.alignments, binding_ty);
2832        let visible_size = align_to(bind_size, bounds_check_alignment);
2833
2834        used_buffer_ranges.extend(buffer.initialization_status.read().create_action(
2835            buffer,
2836            bb.offset..bb.offset + visible_size,
2837            MemoryInitKind::NeedsInitializedMemory,
2838        ));
2839
2840        Ok(bb)
2841    }
2842
2843    fn create_sampler_binding<'a>(
2844        &self,
2845        used: &mut BindGroupStates,
2846        binding: u32,
2847        decl: &wgt::BindGroupLayoutEntry,
2848        sampler: &'a Arc<Sampler>,
2849    ) -> Result<&'a dyn hal::DynSampler, binding_model::CreateBindGroupError> {
2850        use crate::binding_model::CreateBindGroupError as Error;
2851
2852        used.samplers.insert_single(sampler.clone());
2853
2854        sampler.same_device(self)?;
2855
2856        match decl.ty {
2857            wgt::BindingType::Sampler(ty) => {
2858                let (allowed_filtering, allowed_comparison) = match ty {
2859                    wgt::SamplerBindingType::Filtering => (None, false),
2860                    wgt::SamplerBindingType::NonFiltering => (Some(false), false),
2861                    wgt::SamplerBindingType::Comparison => (None, true),
2862                };
2863                if let Some(allowed_filtering) = allowed_filtering {
2864                    if allowed_filtering != sampler.filtering {
2865                        return Err(Error::WrongSamplerFiltering {
2866                            binding,
2867                            layout_flt: allowed_filtering,
2868                            sampler_flt: sampler.filtering,
2869                        });
2870                    }
2871                }
2872                if allowed_comparison != sampler.comparison {
2873                    return Err(Error::WrongSamplerComparison {
2874                        binding,
2875                        layout_cmp: allowed_comparison,
2876                        sampler_cmp: sampler.comparison,
2877                    });
2878                }
2879            }
2880            _ => {
2881                return Err(Error::WrongBindingType {
2882                    binding,
2883                    actual: decl.ty,
2884                    expected: "Sampler",
2885                })
2886            }
2887        }
2888
2889        Ok(sampler.raw())
2890    }
2891
2892    fn create_texture_binding<'a>(
2893        &self,
2894        binding: u32,
2895        decl: &wgt::BindGroupLayoutEntry,
2896        view: &'a Arc<TextureView>,
2897        used: &mut BindGroupStates,
2898        used_texture_ranges: &mut Vec<TextureInitTrackerAction>,
2899        snatch_guard: &'a SnatchGuard<'a>,
2900    ) -> Result<hal::TextureBinding<'a, dyn hal::DynTextureView>, binding_model::CreateBindGroupError>
2901    {
2902        view.same_device(self)?;
2903
2904        let internal_use = self.texture_use_parameters(
2905            binding,
2906            decl,
2907            view,
2908            "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
2909        )?;
2910
2911        used.views.insert_single(view.clone(), internal_use);
2912
2913        let texture = &view.parent;
2914
2915        used_texture_ranges.push(TextureInitTrackerAction {
2916            texture: texture.clone(),
2917            range: TextureInitRange {
2918                mip_range: view.desc.range.mip_range(texture.desc.mip_level_count),
2919                layer_range: view
2920                    .desc
2921                    .range
2922                    .layer_range(texture.desc.array_layer_count()),
2923            },
2924            kind: MemoryInitKind::NeedsInitializedMemory,
2925        });
2926
2927        Ok(hal::TextureBinding {
2928            view: view.try_raw(snatch_guard)?,
2929            usage: internal_use,
2930        })
2931    }
2932
2933    fn create_tlas_binding<'a>(
2934        self: &Arc<Self>,
2935        used: &mut BindGroupStates,
2936        binding: u32,
2937        decl: &wgt::BindGroupLayoutEntry,
2938        tlas: &'a Arc<Tlas>,
2939        snatch_guard: &'a SnatchGuard<'a>,
2940    ) -> Result<&'a dyn hal::DynAccelerationStructure, binding_model::CreateBindGroupError> {
2941        use crate::binding_model::CreateBindGroupError as Error;
2942
2943        used.acceleration_structures.insert_single(tlas.clone());
2944
2945        tlas.same_device(self)?;
2946
2947        match decl.ty {
2948            wgt::BindingType::AccelerationStructure { vertex_return } => {
2949                if vertex_return
2950                    && !tlas.flags.contains(
2951                        wgpu_types::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,
2952                    )
2953                {
2954                    return Err(Error::MissingTLASVertexReturn { binding });
2955                }
2956            }
2957            _ => {
2958                return Err(Error::WrongBindingType {
2959                    binding,
2960                    actual: decl.ty,
2961                    expected: "Tlas",
2962                });
2963            }
2964        }
2965
2966        Ok(tlas.try_raw(snatch_guard)?)
2967    }
2968
2969    fn create_external_texture_binding<'a>(
2970        &'a self,
2971        binding: u32,
2972        decl: &wgt::BindGroupLayoutEntry,
2973        external_texture: &'a Arc<ExternalTexture>,
2974        used: &mut BindGroupStates,
2975        snatch_guard: &'a SnatchGuard,
2976    ) -> Result<
2977        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
2978        binding_model::CreateBindGroupError,
2979    > {
2980        use crate::binding_model::CreateBindGroupError as Error;
2981
2982        external_texture.same_device(self)?;
2983
2984        used.external_textures
2985            .insert_single(external_texture.clone());
2986
2987        match decl.ty {
2988            wgt::BindingType::ExternalTexture => {}
2989            _ => {
2990                return Err(Error::WrongBindingType {
2991                    binding,
2992                    actual: decl.ty,
2993                    expected: "ExternalTexture",
2994                });
2995            }
2996        }
2997
2998        let planes = (0..3)
2999            .map(|i| {
3000                // We always need 3 bindings. If we have fewer than 3 planes
3001                // just bind plane 0 multiple times. The shader will only
3002                // sample from valid planes anyway.
3003                let plane = external_texture
3004                    .planes
3005                    .get(i)
3006                    .unwrap_or(&external_texture.planes[0]);
3007                let internal_use = wgt::TextureUses::RESOURCE;
3008                used.views.insert_single(plane.clone(), internal_use);
3009                let view = plane.try_raw(snatch_guard)?;
3010                Ok(hal::TextureBinding {
3011                    view,
3012                    usage: internal_use,
3013                })
3014            })
3015            // We can remove this intermediate Vec by using
3016            // array::try_from_fn() above, once it stabilizes.
3017            .collect::<Result<Vec<_>, Error>>()?;
3018        let planes = planes.try_into().unwrap();
3019
3020        used.buffers
3021            .insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);
3022        let params = external_texture.params.binding(0, None, snatch_guard)?.0;
3023
3024        Ok(hal::ExternalTextureBinding { planes, params })
3025    }
3026
3027    fn create_external_texture_binding_from_view<'a>(
3028        &'a self,
3029        binding: u32,
3030        decl: &wgt::BindGroupLayoutEntry,
3031        view: &'a Arc<TextureView>,
3032        used: &mut BindGroupStates,
3033        snatch_guard: &'a SnatchGuard,
3034    ) -> Result<
3035        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3036        binding_model::CreateBindGroupError,
3037    > {
3038        use crate::binding_model::CreateBindGroupError as Error;
3039
3040        view.same_device(self)?;
3041
3042        let internal_use = self.texture_use_parameters(binding, decl, view, "SampledTexture")?;
3043        used.views.insert_single(view.clone(), internal_use);
3044
3045        match decl.ty {
3046            wgt::BindingType::ExternalTexture => {}
3047            _ => {
3048                return Err(Error::WrongBindingType {
3049                    binding,
3050                    actual: decl.ty,
3051                    expected: "ExternalTexture",
3052                });
3053            }
3054        }
3055
3056        // We need 3 bindings, so just repeat the same texture view 3 times.
3057        let planes = [
3058            hal::TextureBinding {
3059                view: view.try_raw(snatch_guard)?,
3060                usage: internal_use,
3061            },
3062            hal::TextureBinding {
3063                view: view.try_raw(snatch_guard)?,
3064                usage: internal_use,
3065            },
3066            hal::TextureBinding {
3067                view: view.try_raw(snatch_guard)?,
3068                usage: internal_use,
3069            },
3070        ];
3071        let params = hal::BufferBinding::new_unchecked(
3072            self.default_external_texture_params_buffer.as_ref(),
3073            0,
3074            None,
3075        );
3076
3077        Ok(hal::ExternalTextureBinding { planes, params })
3078    }
3079
3080    // This function expects the provided bind group layout to be resolved
3081    // (not passing a duplicate) beforehand.
3082    pub fn create_bind_group(
3083        self: &Arc<Self>,
3084        desc: binding_model::ResolvedBindGroupDescriptor,
3085    ) -> Result<Arc<BindGroup>, binding_model::CreateBindGroupError> {
3086        use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};
3087
3088        let layout = desc.layout;
3089
3090        self.check_is_valid()?;
3091        layout.same_device(self)?;
3092
3093        {
3094            // Check that the number of entries in the descriptor matches
3095            // the number of entries in the layout.
3096            let actual = desc.entries.len();
3097            let expected = layout.entries.len();
3098            if actual != expected {
3099                return Err(Error::BindingsNumMismatch { expected, actual });
3100            }
3101        }
3102
3103        // TODO: arrayvec/smallvec, or re-use allocations
3104        // Record binding info for dynamic offset validation
3105        let mut dynamic_binding_info = Vec::new();
3106        // Map of binding -> shader reflected size
3107        //Note: we can't collect into a vector right away because
3108        // it needs to be in BGL iteration order, not BG entry order.
3109        let mut late_buffer_binding_sizes = FastHashMap::default();
3110        // fill out the descriptors
3111        let mut used = BindGroupStates::new();
3112
3113        let mut used_buffer_ranges = Vec::new();
3114        let mut used_texture_ranges = Vec::new();
3115        let mut hal_entries = Vec::with_capacity(desc.entries.len());
3116        let mut hal_buffers = Vec::new();
3117        let mut hal_samplers = Vec::new();
3118        let mut hal_textures = Vec::new();
3119        let mut hal_tlas_s = Vec::new();
3120        let mut hal_external_textures = Vec::new();
3121        let snatch_guard = self.snatchable_lock.read();
3122        for entry in desc.entries.iter() {
3123            let binding = entry.binding;
3124            // Find the corresponding declaration in the layout
3125            let decl = layout
3126                .entries
3127                .get(binding)
3128                .ok_or(Error::MissingBindingDeclaration(binding))?;
3129            let (res_index, count) = match entry.resource {
3130                Br::Buffer(ref bb) => {
3131                    let bb = self.create_buffer_binding(
3132                        bb,
3133                        binding,
3134                        decl,
3135                        &mut used_buffer_ranges,
3136                        &mut dynamic_binding_info,
3137                        &mut late_buffer_binding_sizes,
3138                        &mut used,
3139                        &snatch_guard,
3140                    )?;
3141
3142                    let res_index = hal_buffers.len();
3143                    hal_buffers.push(bb);
3144                    (res_index, 1)
3145                }
3146                Br::BufferArray(ref bindings_array) => {
3147                    let num_bindings = bindings_array.len();
3148                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3149
3150                    let res_index = hal_buffers.len();
3151                    for bb in bindings_array.iter() {
3152                        let bb = self.create_buffer_binding(
3153                            bb,
3154                            binding,
3155                            decl,
3156                            &mut used_buffer_ranges,
3157                            &mut dynamic_binding_info,
3158                            &mut late_buffer_binding_sizes,
3159                            &mut used,
3160                            &snatch_guard,
3161                        )?;
3162                        hal_buffers.push(bb);
3163                    }
3164                    (res_index, num_bindings)
3165                }
3166                Br::Sampler(ref sampler) => {
3167                    let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3168
3169                    let res_index = hal_samplers.len();
3170                    hal_samplers.push(sampler);
3171                    (res_index, 1)
3172                }
3173                Br::SamplerArray(ref samplers) => {
3174                    let num_bindings = samplers.len();
3175                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3176
3177                    let res_index = hal_samplers.len();
3178                    for sampler in samplers.iter() {
3179                        let sampler =
3180                            self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3181
3182                        hal_samplers.push(sampler);
3183                    }
3184
3185                    (res_index, num_bindings)
3186                }
3187                Br::TextureView(ref view) => match decl.ty {
3188                    wgt::BindingType::ExternalTexture => {
3189                        let et = self.create_external_texture_binding_from_view(
3190                            binding,
3191                            decl,
3192                            view,
3193                            &mut used,
3194                            &snatch_guard,
3195                        )?;
3196                        let res_index = hal_external_textures.len();
3197                        hal_external_textures.push(et);
3198                        (res_index, 1)
3199                    }
3200                    _ => {
3201                        let tb = self.create_texture_binding(
3202                            binding,
3203                            decl,
3204                            view,
3205                            &mut used,
3206                            &mut used_texture_ranges,
3207                            &snatch_guard,
3208                        )?;
3209                        let res_index = hal_textures.len();
3210                        hal_textures.push(tb);
3211                        (res_index, 1)
3212                    }
3213                },
3214                Br::TextureViewArray(ref views) => {
3215                    let num_bindings = views.len();
3216                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3217
3218                    let res_index = hal_textures.len();
3219                    for view in views.iter() {
3220                        let tb = self.create_texture_binding(
3221                            binding,
3222                            decl,
3223                            view,
3224                            &mut used,
3225                            &mut used_texture_ranges,
3226                            &snatch_guard,
3227                        )?;
3228
3229                        hal_textures.push(tb);
3230                    }
3231
3232                    (res_index, num_bindings)
3233                }
3234                Br::AccelerationStructure(ref tlas) => {
3235                    let tlas =
3236                        self.create_tlas_binding(&mut used, binding, decl, tlas, &snatch_guard)?;
3237                    let res_index = hal_tlas_s.len();
3238                    hal_tlas_s.push(tlas);
3239                    (res_index, 1)
3240                }
3241                Br::ExternalTexture(ref et) => {
3242                    let et = self.create_external_texture_binding(
3243                        binding,
3244                        decl,
3245                        et,
3246                        &mut used,
3247                        &snatch_guard,
3248                    )?;
3249                    let res_index = hal_external_textures.len();
3250                    hal_external_textures.push(et);
3251                    (res_index, 1)
3252                }
3253            };
3254
3255            hal_entries.push(hal::BindGroupEntry {
3256                binding,
3257                resource_index: res_index as u32,
3258                count: count as u32,
3259            });
3260        }
3261
3262        used.optimize();
3263
3264        hal_entries.sort_by_key(|entry| entry.binding);
3265        for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
3266            if a.binding == b.binding {
3267                return Err(Error::DuplicateBinding(a.binding));
3268            }
3269        }
3270        let hal_desc = hal::BindGroupDescriptor {
3271            label: desc.label.to_hal(self.instance_flags),
3272            layout: layout.raw(),
3273            entries: &hal_entries,
3274            buffers: &hal_buffers,
3275            samplers: &hal_samplers,
3276            textures: &hal_textures,
3277            acceleration_structures: &hal_tlas_s,
3278            external_textures: &hal_external_textures,
3279        };
3280        let raw = unsafe { self.raw().create_bind_group(&hal_desc) }
3281            .map_err(|e| self.handle_hal_error(e))?;
3282
3283        // collect in the order of BGL iteration
3284        let late_buffer_binding_infos = layout
3285            .entries
3286            .indices()
3287            .flat_map(|binding| {
3288                let size = late_buffer_binding_sizes.get(&binding).cloned()?;
3289                Some(BindGroupLateBufferBindingInfo {
3290                    binding_index: binding,
3291                    size,
3292                })
3293            })
3294            .collect();
3295
3296        let bind_group = BindGroup {
3297            raw: Snatchable::new(raw),
3298            device: self.clone(),
3299            layout,
3300            label: desc.label.to_string(),
3301            tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),
3302            used,
3303            used_buffer_ranges,
3304            used_texture_ranges,
3305            dynamic_binding_info,
3306            late_buffer_binding_infos,
3307        };
3308
3309        let bind_group = Arc::new(bind_group);
3310
3311        let weak_ref = Arc::downgrade(&bind_group);
3312        for range in &bind_group.used_texture_ranges {
3313            let mut bind_groups = range.texture.bind_groups.lock();
3314            bind_groups.push(weak_ref.clone());
3315        }
3316        for range in &bind_group.used_buffer_ranges {
3317            let mut bind_groups = range.buffer.bind_groups.lock();
3318            bind_groups.push(weak_ref.clone());
3319        }
3320
3321        Ok(bind_group)
3322    }
3323
3324    fn check_array_binding(
3325        features: wgt::Features,
3326        count: Option<NonZeroU32>,
3327        num_bindings: usize,
3328    ) -> Result<(), binding_model::CreateBindGroupError> {
3329        use super::binding_model::CreateBindGroupError as Error;
3330
3331        if let Some(count) = count {
3332            let count = count.get() as usize;
3333            if count < num_bindings {
3334                return Err(Error::BindingArrayPartialLengthMismatch {
3335                    actual: num_bindings,
3336                    expected: count,
3337                });
3338            }
3339            if count != num_bindings
3340                && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
3341            {
3342                return Err(Error::BindingArrayLengthMismatch {
3343                    actual: num_bindings,
3344                    expected: count,
3345                });
3346            }
3347            if num_bindings == 0 {
3348                return Err(Error::BindingArrayZeroLength);
3349            }
3350        } else {
3351            return Err(Error::SingleBindingExpected);
3352        };
3353
3354        Ok(())
3355    }
3356
3357    fn texture_use_parameters(
3358        &self,
3359        binding: u32,
3360        decl: &wgt::BindGroupLayoutEntry,
3361        view: &TextureView,
3362        expected: &'static str,
3363    ) -> Result<wgt::TextureUses, binding_model::CreateBindGroupError> {
3364        use crate::binding_model::CreateBindGroupError as Error;
3365        if view
3366            .desc
3367            .aspects()
3368            .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
3369        {
3370            return Err(Error::DepthStencilAspect);
3371        }
3372        match decl.ty {
3373            wgt::BindingType::Texture {
3374                sample_type,
3375                view_dimension,
3376                multisampled,
3377            } => {
3378                use wgt::TextureSampleType as Tst;
3379                if multisampled != (view.samples != 1) {
3380                    return Err(Error::InvalidTextureMultisample {
3381                        binding,
3382                        layout_multisampled: multisampled,
3383                        view_samples: view.samples,
3384                    });
3385                }
3386                let compat_sample_type = view
3387                    .desc
3388                    .format
3389                    .sample_type(Some(view.desc.range.aspect), Some(self.features))
3390                    .unwrap();
3391                match (sample_type, compat_sample_type) {
3392                    (Tst::Uint, Tst::Uint) |
3393                        (Tst::Sint, Tst::Sint) |
3394                        (Tst::Depth, Tst::Depth) |
3395                        // if we expect non-filterable, accept anything float
3396                        (Tst::Float { filterable: false }, Tst::Float { .. }) |
3397                        // if we expect filterable, require it
3398                        (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
3399                        // if we expect non-filterable, also accept depth
3400                        (Tst::Float { filterable: false }, Tst::Depth) => {}
3401                    // if we expect filterable, also accept Float that is defined as
3402                    // unfilterable if filterable feature is explicitly enabled (only hit
3403                    // if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is
3404                    // enabled)
3405                    (Tst::Float { filterable: true }, Tst::Float { .. }) if view.format_features.flags.contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
3406                    _ => {
3407                        return Err(Error::InvalidTextureSampleType {
3408                            binding,
3409                            layout_sample_type: sample_type,
3410                            view_format: view.desc.format,
3411                            view_sample_type: compat_sample_type,
3412                        })
3413                    }
3414                }
3415                if view_dimension != view.desc.dimension {
3416                    return Err(Error::InvalidTextureDimension {
3417                        binding,
3418                        layout_dimension: view_dimension,
3419                        view_dimension: view.desc.dimension,
3420                    });
3421                }
3422                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3423                Ok(wgt::TextureUses::RESOURCE)
3424            }
3425            wgt::BindingType::StorageTexture {
3426                access,
3427                format,
3428                view_dimension,
3429            } => {
3430                if format != view.desc.format {
3431                    return Err(Error::InvalidStorageTextureFormat {
3432                        binding,
3433                        layout_format: format,
3434                        view_format: view.desc.format,
3435                    });
3436                }
3437                if view_dimension != view.desc.dimension {
3438                    return Err(Error::InvalidTextureDimension {
3439                        binding,
3440                        layout_dimension: view_dimension,
3441                        view_dimension: view.desc.dimension,
3442                    });
3443                }
3444
3445                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3446                if mip_level_count != 1 {
3447                    return Err(Error::InvalidStorageTextureMipLevelCount {
3448                        binding,
3449                        mip_level_count,
3450                    });
3451                }
3452
3453                let internal_use = match access {
3454                    wgt::StorageTextureAccess::WriteOnly => {
3455                        if !view
3456                            .format_features
3457                            .flags
3458                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY)
3459                        {
3460                            return Err(Error::StorageWriteNotSupported(view.desc.format));
3461                        }
3462                        wgt::TextureUses::STORAGE_WRITE_ONLY
3463                    }
3464                    wgt::StorageTextureAccess::ReadOnly => {
3465                        if !view
3466                            .format_features
3467                            .flags
3468                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY)
3469                        {
3470                            return Err(Error::StorageReadNotSupported(view.desc.format));
3471                        }
3472                        wgt::TextureUses::STORAGE_READ_ONLY
3473                    }
3474                    wgt::StorageTextureAccess::ReadWrite => {
3475                        if !view
3476                            .format_features
3477                            .flags
3478                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE)
3479                        {
3480                            return Err(Error::StorageReadWriteNotSupported(view.desc.format));
3481                        }
3482
3483                        wgt::TextureUses::STORAGE_READ_WRITE
3484                    }
3485                    wgt::StorageTextureAccess::Atomic => {
3486                        if !view
3487                            .format_features
3488                            .flags
3489                            .contains(wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC)
3490                        {
3491                            return Err(Error::StorageAtomicNotSupported(view.desc.format));
3492                        }
3493
3494                        wgt::TextureUses::STORAGE_ATOMIC
3495                    }
3496                };
3497                view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;
3498                Ok(internal_use)
3499            }
3500            wgt::BindingType::ExternalTexture => {
3501                if view.desc.dimension != TextureViewDimension::D2 {
3502                    return Err(Error::InvalidTextureDimension {
3503                        binding,
3504                        layout_dimension: TextureViewDimension::D2,
3505                        view_dimension: view.desc.dimension,
3506                    });
3507                }
3508                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3509                if mip_level_count != 1 {
3510                    return Err(Error::InvalidExternalTextureMipLevelCount {
3511                        binding,
3512                        mip_level_count,
3513                    });
3514                }
3515                if view.desc.format != TextureFormat::Rgba8Unorm
3516                    && view.desc.format != TextureFormat::Bgra8Unorm
3517                    && view.desc.format != TextureFormat::Rgba16Float
3518                {
3519                    return Err(Error::InvalidExternalTextureFormat {
3520                        binding,
3521                        format: view.desc.format,
3522                    });
3523                }
3524                if view.samples != 1 {
3525                    return Err(Error::InvalidTextureMultisample {
3526                        binding,
3527                        layout_multisampled: false,
3528                        view_samples: view.samples,
3529                    });
3530                }
3531
3532                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3533                Ok(wgt::TextureUses::RESOURCE)
3534            }
3535            _ => Err(Error::WrongBindingType {
3536                binding,
3537                actual: decl.ty,
3538                expected,
3539            }),
3540        }
3541    }
3542
3543    pub fn create_pipeline_layout(
3544        self: &Arc<Self>,
3545        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3546    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
3547        use crate::binding_model::CreatePipelineLayoutError as Error;
3548
3549        self.check_is_valid()?;
3550
3551        let bind_group_layouts_count = desc.bind_group_layouts.len();
3552        let device_max_bind_groups = self.limits.max_bind_groups as usize;
3553        if bind_group_layouts_count > device_max_bind_groups {
3554            return Err(Error::TooManyGroups {
3555                actual: bind_group_layouts_count,
3556                max: device_max_bind_groups,
3557            });
3558        }
3559
3560        if !desc.push_constant_ranges.is_empty() {
3561            self.require_features(wgt::Features::PUSH_CONSTANTS)?;
3562        }
3563
3564        let mut used_stages = wgt::ShaderStages::empty();
3565        for (index, pc) in desc.push_constant_ranges.iter().enumerate() {
3566            if pc.stages.intersects(used_stages) {
3567                return Err(Error::MoreThanOnePushConstantRangePerStage {
3568                    index,
3569                    provided: pc.stages,
3570                    intersected: pc.stages & used_stages,
3571                });
3572            }
3573            used_stages |= pc.stages;
3574
3575            let device_max_pc_size = self.limits.max_push_constant_size;
3576            if device_max_pc_size < pc.range.end {
3577                return Err(Error::PushConstantRangeTooLarge {
3578                    index,
3579                    range: pc.range.clone(),
3580                    max: device_max_pc_size,
3581                });
3582            }
3583
3584            if pc.range.start % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
3585                return Err(Error::MisalignedPushConstantRange {
3586                    index,
3587                    bound: pc.range.start,
3588                });
3589            }
3590            if pc.range.end % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
3591                return Err(Error::MisalignedPushConstantRange {
3592                    index,
3593                    bound: pc.range.end,
3594                });
3595            }
3596        }
3597
3598        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
3599
3600        for bgl in desc.bind_group_layouts.iter() {
3601            bgl.same_device(self)?;
3602            count_validator.merge(&bgl.binding_count_validator);
3603        }
3604
3605        count_validator
3606            .validate(&self.limits)
3607            .map_err(Error::TooManyBindings)?;
3608
3609        let bind_group_layouts = desc
3610            .bind_group_layouts
3611            .iter()
3612            .cloned()
3613            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3614
3615        let raw_bind_group_layouts = desc
3616            .bind_group_layouts
3617            .iter()
3618            .map(|bgl| bgl.raw())
3619            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3620
3621        let additional_flags = if self.indirect_validation.is_some() {
3622            hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE
3623        } else {
3624            hal::PipelineLayoutFlags::empty()
3625        };
3626
3627        let hal_desc = hal::PipelineLayoutDescriptor {
3628            label: desc.label.to_hal(self.instance_flags),
3629            flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
3630                | hal::PipelineLayoutFlags::NUM_WORK_GROUPS
3631                | additional_flags,
3632            bind_group_layouts: &raw_bind_group_layouts,
3633            push_constant_ranges: desc.push_constant_ranges.as_ref(),
3634        };
3635
3636        let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }
3637            .map_err(|e| self.handle_hal_error(e))?;
3638
3639        drop(raw_bind_group_layouts);
3640
3641        let layout = binding_model::PipelineLayout {
3642            raw: ManuallyDrop::new(raw),
3643            device: self.clone(),
3644            label: desc.label.to_string(),
3645            bind_group_layouts,
3646            push_constant_ranges: desc.push_constant_ranges.iter().cloned().collect(),
3647        };
3648
3649        let layout = Arc::new(layout);
3650
3651        Ok(layout)
3652    }
3653
3654    pub(crate) fn derive_pipeline_layout(
3655        self: &Arc<Self>,
3656        mut derived_group_layouts: Box<ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>>,
3657    ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {
3658        while derived_group_layouts
3659            .last()
3660            .is_some_and(|map| map.is_empty())
3661        {
3662            derived_group_layouts.pop();
3663        }
3664
3665        let mut unique_bind_group_layouts = FastHashMap::default();
3666
3667        let bind_group_layouts = derived_group_layouts
3668            .into_iter()
3669            .map(|mut bgl_entry_map| {
3670                bgl_entry_map.sort();
3671                match unique_bind_group_layouts.entry(bgl_entry_map) {
3672                    hashbrown::hash_map::Entry::Occupied(v) => Ok(Arc::clone(v.get())),
3673                    hashbrown::hash_map::Entry::Vacant(e) => {
3674                        match self.create_bind_group_layout_internal(
3675                            &None,
3676                            e.key().clone(),
3677                            bgl::Origin::Derived,
3678                        ) {
3679                            Ok(bgl) => {
3680                                e.insert(bgl.clone());
3681                                Ok(bgl)
3682                            }
3683                            Err(e) => Err(e),
3684                        }
3685                    }
3686                }
3687            })
3688            .collect::<Result<Vec<_>, _>>()?;
3689
3690        let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {
3691            label: None,
3692            bind_group_layouts: Cow::Owned(bind_group_layouts),
3693            push_constant_ranges: Cow::Borrowed(&[]), //TODO?
3694        };
3695
3696        let layout = self.create_pipeline_layout(&layout_desc)?;
3697        Ok(layout)
3698    }
3699
3700    pub fn create_compute_pipeline(
3701        self: &Arc<Self>,
3702        desc: pipeline::ResolvedComputePipelineDescriptor,
3703    ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {
3704        self.check_is_valid()?;
3705
3706        self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
3707
3708        let shader_module = desc.stage.module;
3709
3710        shader_module.same_device(self)?;
3711
3712        let is_auto_layout = desc.layout.is_none();
3713
3714        // Get the pipeline layout from the desc if it is provided.
3715        let pipeline_layout = match desc.layout {
3716            Some(pipeline_layout) => {
3717                pipeline_layout.same_device(self)?;
3718                Some(pipeline_layout)
3719            }
3720            None => None,
3721        };
3722
3723        let mut binding_layout_source = match pipeline_layout {
3724            Some(ref pipeline_layout) => {
3725                validation::BindingLayoutSource::Provided(pipeline_layout.get_binding_maps())
3726            }
3727            None => validation::BindingLayoutSource::new_derived(&self.limits),
3728        };
3729        let mut shader_binding_sizes = FastHashMap::default();
3730        let io = validation::StageIo::default();
3731
3732        let final_entry_point_name;
3733
3734        {
3735            let stage = wgt::ShaderStages::COMPUTE;
3736
3737            final_entry_point_name = shader_module.finalize_entry_point_name(
3738                stage,
3739                desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
3740            )?;
3741
3742            if let Some(ref interface) = shader_module.interface {
3743                let _ = interface.check_stage(
3744                    &mut binding_layout_source,
3745                    &mut shader_binding_sizes,
3746                    &final_entry_point_name,
3747                    stage,
3748                    io,
3749                    None,
3750                )?;
3751            }
3752        }
3753
3754        let pipeline_layout = match binding_layout_source {
3755            validation::BindingLayoutSource::Provided(_) => {
3756                drop(binding_layout_source);
3757                pipeline_layout.unwrap()
3758            }
3759            validation::BindingLayoutSource::Derived(entries) => {
3760                self.derive_pipeline_layout(entries)?
3761            }
3762        };
3763
3764        let late_sized_buffer_groups =
3765            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
3766
3767        let cache = match desc.cache {
3768            Some(cache) => {
3769                cache.same_device(self)?;
3770                Some(cache)
3771            }
3772            None => None,
3773        };
3774
3775        let pipeline_desc = hal::ComputePipelineDescriptor {
3776            label: desc.label.to_hal(self.instance_flags),
3777            layout: pipeline_layout.raw(),
3778            stage: hal::ProgrammableStage {
3779                module: shader_module.raw(),
3780                entry_point: final_entry_point_name.as_ref(),
3781                constants: &desc.stage.constants,
3782                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
3783            },
3784            cache: cache.as_ref().map(|it| it.raw()),
3785        };
3786
3787        let raw =
3788            unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(
3789                |err| match err {
3790                    hal::PipelineError::Device(error) => {
3791                        pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))
3792                    }
3793                    hal::PipelineError::Linkage(_stages, msg) => {
3794                        pipeline::CreateComputePipelineError::Internal(msg)
3795                    }
3796                    hal::PipelineError::EntryPoint(_stage) => {
3797                        pipeline::CreateComputePipelineError::Internal(
3798                            ENTRYPOINT_FAILURE_ERROR.to_string(),
3799                        )
3800                    }
3801                    hal::PipelineError::PipelineConstants(_stages, msg) => {
3802                        pipeline::CreateComputePipelineError::PipelineConstants(msg)
3803                    }
3804                },
3805            )?;
3806
3807        let pipeline = pipeline::ComputePipeline {
3808            raw: ManuallyDrop::new(raw),
3809            layout: pipeline_layout,
3810            device: self.clone(),
3811            _shader_module: shader_module,
3812            late_sized_buffer_groups,
3813            label: desc.label.to_string(),
3814            tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),
3815        };
3816
3817        let pipeline = Arc::new(pipeline);
3818
3819        if is_auto_layout {
3820            for bgl in pipeline.layout.bind_group_layouts.iter() {
3821                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
3822                let _ = bgl
3823                    .exclusive_pipeline
3824                    .set(binding_model::ExclusivePipeline::Compute(Arc::downgrade(
3825                        &pipeline,
3826                    )));
3827            }
3828        }
3829
3830        Ok(pipeline)
3831    }
3832
3833    pub fn create_render_pipeline(
3834        self: &Arc<Self>,
3835        desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
3836    ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {
3837        use wgt::TextureFormatFeatureFlags as Tfff;
3838
3839        self.check_is_valid()?;
3840
3841        let mut shader_binding_sizes = FastHashMap::default();
3842
3843        let num_attachments = desc.fragment.as_ref().map(|f| f.targets.len()).unwrap_or(0);
3844        let max_attachments = self.limits.max_color_attachments as usize;
3845        if num_attachments > max_attachments {
3846            return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
3847                command::ColorAttachmentError::TooMany {
3848                    given: num_attachments,
3849                    limit: max_attachments,
3850                },
3851            ));
3852        }
3853
3854        let color_targets = desc
3855            .fragment
3856            .as_ref()
3857            .map_or(&[][..], |fragment| &fragment.targets);
3858        let depth_stencil_state = desc.depth_stencil.as_ref();
3859
3860        {
3861            let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
3862                color_targets.iter().filter_map(|x| x.as_ref()).collect();
3863            if !cts.is_empty() && {
3864                let first = &cts[0];
3865                cts[1..]
3866                    .iter()
3867                    .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
3868            } {
3869                self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
3870            }
3871        }
3872
3873        let mut io = validation::StageIo::default();
3874        let mut validated_stages = wgt::ShaderStages::empty();
3875
3876        let mut vertex_steps;
3877        let mut vertex_buffers;
3878        let mut total_attributes;
3879        let mut shader_expects_dual_source_blending = false;
3880        let mut pipeline_expects_dual_source_blending = false;
3881        if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
3882            vertex_steps = Vec::with_capacity(vertex.buffers.len());
3883            vertex_buffers = Vec::with_capacity(vertex.buffers.len());
3884            total_attributes = 0;
3885            shader_expects_dual_source_blending = false;
3886            pipeline_expects_dual_source_blending = false;
3887            for (i, vb_state) in vertex.buffers.iter().enumerate() {
3888                // https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpuvertexbufferlayout
3889
3890                if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
3891                    return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
3892                        index: i as u32,
3893                        given: vb_state.array_stride as u32,
3894                        limit: self.limits.max_vertex_buffer_array_stride,
3895                    });
3896                }
3897                if vb_state.array_stride % wgt::VERTEX_ALIGNMENT != 0 {
3898                    return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
3899                        index: i as u32,
3900                        stride: vb_state.array_stride,
3901                    });
3902                }
3903
3904                let max_stride = if vb_state.array_stride == 0 {
3905                    self.limits.max_vertex_buffer_array_stride as u64
3906                } else {
3907                    vb_state.array_stride
3908                };
3909                let mut last_stride = 0;
3910                for attribute in vb_state.attributes.iter() {
3911                    let attribute_stride = attribute.offset + attribute.format.size();
3912                    if attribute_stride > max_stride {
3913                        return Err(
3914                            pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge {
3915                                location: attribute.shader_location,
3916                                given: attribute_stride as u32,
3917                                limit: max_stride as u32,
3918                            },
3919                        );
3920                    }
3921
3922                    let required_offset_alignment = attribute.format.size().min(4);
3923                    if attribute.offset % required_offset_alignment != 0 {
3924                        return Err(
3925                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
3926                                location: attribute.shader_location,
3927                                offset: attribute.offset,
3928                            },
3929                        );
3930                    }
3931
3932                    if attribute.shader_location >= self.limits.max_vertex_attributes {
3933                        return Err(
3934                            pipeline::CreateRenderPipelineError::VertexAttributeLocationTooLarge {
3935                                given: attribute.shader_location,
3936                                limit: self.limits.max_vertex_attributes,
3937                            },
3938                        );
3939                    }
3940
3941                    last_stride = last_stride.max(attribute_stride);
3942                }
3943                vertex_steps.push(pipeline::VertexStep {
3944                    stride: vb_state.array_stride,
3945                    last_stride,
3946                    mode: vb_state.step_mode,
3947                });
3948                if vb_state.attributes.is_empty() {
3949                    continue;
3950                }
3951                vertex_buffers.push(hal::VertexBufferLayout {
3952                    array_stride: vb_state.array_stride,
3953                    step_mode: vb_state.step_mode,
3954                    attributes: vb_state.attributes.as_ref(),
3955                });
3956
3957                for attribute in vb_state.attributes.iter() {
3958                    if attribute.offset >= 0x10000000 {
3959                        return Err(
3960                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
3961                                location: attribute.shader_location,
3962                                offset: attribute.offset,
3963                            },
3964                        );
3965                    }
3966
3967                    if let wgt::VertexFormat::Float64
3968                    | wgt::VertexFormat::Float64x2
3969                    | wgt::VertexFormat::Float64x3
3970                    | wgt::VertexFormat::Float64x4 = attribute.format
3971                    {
3972                        self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
3973                    }
3974
3975                    let previous = io.insert(
3976                        attribute.shader_location,
3977                        validation::InterfaceVar::vertex_attribute(attribute.format),
3978                    );
3979
3980                    if previous.is_some() {
3981                        return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
3982                            attribute.shader_location,
3983                        ));
3984                    }
3985                }
3986                total_attributes += vb_state.attributes.len();
3987            }
3988
3989            if vertex_buffers.len() > self.limits.max_vertex_buffers as usize {
3990                return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
3991                    given: vertex_buffers.len() as u32,
3992                    limit: self.limits.max_vertex_buffers,
3993                });
3994            }
3995            if total_attributes > self.limits.max_vertex_attributes as usize {
3996                return Err(
3997                    pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
3998                        given: total_attributes as u32,
3999                        limit: self.limits.max_vertex_attributes,
4000                    },
4001                );
4002            }
4003        } else {
4004            vertex_steps = Vec::new();
4005            vertex_buffers = Vec::new();
4006        };
4007
4008        if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
4009            return Err(
4010                pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
4011                    strip_index_format: desc.primitive.strip_index_format,
4012                    topology: desc.primitive.topology,
4013                },
4014            );
4015        }
4016
4017        if desc.primitive.unclipped_depth {
4018            self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
4019        }
4020
4021        if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
4022            self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
4023        }
4024        if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
4025            self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
4026        }
4027
4028        if desc.primitive.conservative {
4029            self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
4030        }
4031
4032        if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
4033            return Err(
4034                pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
4035            );
4036        }
4037
4038        let mut target_specified = false;
4039
4040        for (i, cs) in color_targets.iter().enumerate() {
4041            if let Some(cs) = cs.as_ref() {
4042                target_specified = true;
4043                let error = 'error: {
4044                    if cs.write_mask.contains_unknown_bits() {
4045                        break 'error Some(pipeline::ColorStateError::InvalidWriteMask(
4046                            cs.write_mask,
4047                        ));
4048                    }
4049
4050                    let format_features = self.describe_format_features(cs.format)?;
4051                    if !format_features
4052                        .allowed_usages
4053                        .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4054                    {
4055                        break 'error Some(pipeline::ColorStateError::FormatNotRenderable(
4056                            cs.format,
4057                        ));
4058                    }
4059                    let blendable = format_features.flags.contains(Tfff::BLENDABLE);
4060                    let filterable = format_features.flags.contains(Tfff::FILTERABLE);
4061                    let adapter_specific = self
4062                        .features
4063                        .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
4064                    // according to WebGPU specifications the texture needs to be
4065                    // [`TextureFormatFeatureFlags::FILTERABLE`] if blending is set - use
4066                    // [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] to elude
4067                    // this limitation
4068                    if cs.blend.is_some() && (!blendable || (!filterable && !adapter_specific)) {
4069                        break 'error Some(pipeline::ColorStateError::FormatNotBlendable(
4070                            cs.format,
4071                        ));
4072                    }
4073                    if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
4074                        break 'error Some(pipeline::ColorStateError::FormatNotColor(cs.format));
4075                    }
4076
4077                    if desc.multisample.count > 1
4078                        && !format_features
4079                            .flags
4080                            .sample_count_supported(desc.multisample.count)
4081                    {
4082                        break 'error Some(pipeline::ColorStateError::InvalidSampleCount(
4083                            desc.multisample.count,
4084                            cs.format,
4085                            cs.format
4086                                .guaranteed_format_features(self.features)
4087                                .flags
4088                                .supported_sample_counts(),
4089                            self.adapter
4090                                .get_texture_format_features(cs.format)
4091                                .flags
4092                                .supported_sample_counts(),
4093                        ));
4094                    }
4095
4096                    if let Some(blend_mode) = cs.blend {
4097                        for factor in [
4098                            blend_mode.color.src_factor,
4099                            blend_mode.color.dst_factor,
4100                            blend_mode.alpha.src_factor,
4101                            blend_mode.alpha.dst_factor,
4102                        ] {
4103                            if factor.ref_second_blend_source() {
4104                                self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
4105                                if i == 0 {
4106                                    pipeline_expects_dual_source_blending = true;
4107                                    break;
4108                                } else {
4109                                    return Err(pipeline::CreateRenderPipelineError
4110                                        ::BlendFactorOnUnsupportedTarget { factor, target: i as u32 });
4111                                }
4112                            }
4113                        }
4114                    }
4115
4116                    break 'error None;
4117                };
4118                if let Some(e) = error {
4119                    return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
4120                }
4121            }
4122        }
4123
4124        let limit = self.limits.max_color_attachment_bytes_per_sample;
4125        let formats = color_targets
4126            .iter()
4127            .map(|cs| cs.as_ref().map(|cs| cs.format));
4128        if let Err(total) = validate_color_attachment_bytes_per_sample(formats, limit) {
4129            return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
4130                command::ColorAttachmentError::TooManyBytesPerSample { total, limit },
4131            ));
4132        }
4133
4134        if let Some(ds) = depth_stencil_state {
4135            target_specified = true;
4136            let error = 'error: {
4137                let format_features = self.describe_format_features(ds.format)?;
4138                if !format_features
4139                    .allowed_usages
4140                    .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4141                {
4142                    break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(
4143                        ds.format,
4144                    ));
4145                }
4146
4147                let aspect = hal::FormatAspects::from(ds.format);
4148                if ds.is_depth_enabled() && !aspect.contains(hal::FormatAspects::DEPTH) {
4149                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
4150                }
4151                if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
4152                    break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(
4153                        ds.format,
4154                    ));
4155                }
4156                if desc.multisample.count > 1
4157                    && !format_features
4158                        .flags
4159                        .sample_count_supported(desc.multisample.count)
4160                {
4161                    break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(
4162                        desc.multisample.count,
4163                        ds.format,
4164                        ds.format
4165                            .guaranteed_format_features(self.features)
4166                            .flags
4167                            .supported_sample_counts(),
4168                        self.adapter
4169                            .get_texture_format_features(ds.format)
4170                            .flags
4171                            .supported_sample_counts(),
4172                    ));
4173                }
4174
4175                break 'error None;
4176            };
4177            if let Some(e) = error {
4178                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
4179            }
4180
4181            if ds.bias.clamp != 0.0 {
4182                self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
4183            }
4184        }
4185
4186        if !target_specified {
4187            return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);
4188        }
4189
4190        let is_auto_layout = desc.layout.is_none();
4191
4192        // Get the pipeline layout from the desc if it is provided.
4193        let pipeline_layout = match desc.layout {
4194            Some(pipeline_layout) => {
4195                pipeline_layout.same_device(self)?;
4196                Some(pipeline_layout)
4197            }
4198            None => None,
4199        };
4200
4201        let mut binding_layout_source = match pipeline_layout {
4202            Some(ref pipeline_layout) => {
4203                validation::BindingLayoutSource::Provided(pipeline_layout.get_binding_maps())
4204            }
4205            None => validation::BindingLayoutSource::new_derived(&self.limits),
4206        };
4207
4208        let samples = {
4209            let sc = desc.multisample.count;
4210            if sc == 0 || sc > 32 || !sc.is_power_of_two() {
4211                return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
4212            }
4213            sc
4214        };
4215
4216        let mut vertex_stage = None;
4217        let mut task_stage = None;
4218        let mut mesh_stage = None;
4219        let mut _vertex_entry_point_name = String::new();
4220        let mut _task_entry_point_name = String::new();
4221        let mut _mesh_entry_point_name = String::new();
4222        match desc.vertex {
4223            pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) => {
4224                vertex_stage = {
4225                    let stage_desc = &vertex.stage;
4226                    let stage = wgt::ShaderStages::VERTEX;
4227
4228                    let vertex_shader_module = &stage_desc.module;
4229                    vertex_shader_module.same_device(self)?;
4230
4231                    let stage_err =
4232                        |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
4233
4234                    _vertex_entry_point_name = vertex_shader_module
4235                        .finalize_entry_point_name(
4236                            stage,
4237                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4238                        )
4239                        .map_err(stage_err)?;
4240
4241                    if let Some(ref interface) = vertex_shader_module.interface {
4242                        io = interface
4243                            .check_stage(
4244                                &mut binding_layout_source,
4245                                &mut shader_binding_sizes,
4246                                &_vertex_entry_point_name,
4247                                stage,
4248                                io,
4249                                desc.depth_stencil.as_ref().map(|d| d.depth_compare),
4250                            )
4251                            .map_err(stage_err)?;
4252                        validated_stages |= stage;
4253                    }
4254                    Some(hal::ProgrammableStage {
4255                        module: vertex_shader_module.raw(),
4256                        entry_point: &_vertex_entry_point_name,
4257                        constants: &stage_desc.constants,
4258                        zero_initialize_workgroup_memory: stage_desc
4259                            .zero_initialize_workgroup_memory,
4260                    })
4261                };
4262            }
4263            pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {
4264                self.require_features(wgt::Features::EXPERIMENTAL_MESH_SHADER)?;
4265
4266                task_stage = if let Some(task) = task {
4267                    let stage_desc = &task.stage;
4268                    let stage = wgt::ShaderStages::TASK;
4269                    let task_shader_module = &stage_desc.module;
4270                    task_shader_module.same_device(self)?;
4271
4272                    let stage_err =
4273                        |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
4274
4275                    _task_entry_point_name = task_shader_module
4276                        .finalize_entry_point_name(
4277                            stage,
4278                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4279                        )
4280                        .map_err(stage_err)?;
4281
4282                    if let Some(ref interface) = task_shader_module.interface {
4283                        io = interface
4284                            .check_stage(
4285                                &mut binding_layout_source,
4286                                &mut shader_binding_sizes,
4287                                &_task_entry_point_name,
4288                                stage,
4289                                io,
4290                                desc.depth_stencil.as_ref().map(|d| d.depth_compare),
4291                            )
4292                            .map_err(stage_err)?;
4293                        validated_stages |= stage;
4294                    }
4295                    Some(hal::ProgrammableStage {
4296                        module: task_shader_module.raw(),
4297                        entry_point: &_task_entry_point_name,
4298                        constants: &stage_desc.constants,
4299                        zero_initialize_workgroup_memory: stage_desc
4300                            .zero_initialize_workgroup_memory,
4301                    })
4302                } else {
4303                    None
4304                };
4305                mesh_stage = {
4306                    let stage_desc = &mesh.stage;
4307                    let stage = wgt::ShaderStages::MESH;
4308                    let mesh_shader_module = &stage_desc.module;
4309                    mesh_shader_module.same_device(self)?;
4310
4311                    let stage_err =
4312                        |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
4313
4314                    _mesh_entry_point_name = mesh_shader_module
4315                        .finalize_entry_point_name(
4316                            stage,
4317                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4318                        )
4319                        .map_err(stage_err)?;
4320
4321                    if let Some(ref interface) = mesh_shader_module.interface {
4322                        io = interface
4323                            .check_stage(
4324                                &mut binding_layout_source,
4325                                &mut shader_binding_sizes,
4326                                &_mesh_entry_point_name,
4327                                stage,
4328                                io,
4329                                desc.depth_stencil.as_ref().map(|d| d.depth_compare),
4330                            )
4331                            .map_err(stage_err)?;
4332                        validated_stages |= stage;
4333                    }
4334                    Some(hal::ProgrammableStage {
4335                        module: mesh_shader_module.raw(),
4336                        entry_point: &_mesh_entry_point_name,
4337                        constants: &stage_desc.constants,
4338                        zero_initialize_workgroup_memory: stage_desc
4339                            .zero_initialize_workgroup_memory,
4340                    })
4341                };
4342            }
4343        }
4344
4345        let fragment_entry_point_name;
4346        let fragment_stage = match desc.fragment {
4347            Some(ref fragment_state) => {
4348                let stage = wgt::ShaderStages::FRAGMENT;
4349
4350                let shader_module = &fragment_state.stage.module;
4351                shader_module.same_device(self)?;
4352
4353                let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
4354
4355                fragment_entry_point_name = shader_module
4356                    .finalize_entry_point_name(
4357                        stage,
4358                        fragment_state
4359                            .stage
4360                            .entry_point
4361                            .as_ref()
4362                            .map(|ep| ep.as_ref()),
4363                    )
4364                    .map_err(stage_err)?;
4365
4366                if validated_stages == wgt::ShaderStages::VERTEX {
4367                    if let Some(ref interface) = shader_module.interface {
4368                        io = interface
4369                            .check_stage(
4370                                &mut binding_layout_source,
4371                                &mut shader_binding_sizes,
4372                                &fragment_entry_point_name,
4373                                stage,
4374                                io,
4375                                desc.depth_stencil.as_ref().map(|d| d.depth_compare),
4376                            )
4377                            .map_err(stage_err)?;
4378                        validated_stages |= stage;
4379                    }
4380                }
4381
4382                if let Some(ref interface) = shader_module.interface {
4383                    shader_expects_dual_source_blending = interface
4384                        .fragment_uses_dual_source_blending(&fragment_entry_point_name)
4385                        .map_err(|error| pipeline::CreateRenderPipelineError::Stage {
4386                            stage,
4387                            error,
4388                        })?;
4389                }
4390
4391                Some(hal::ProgrammableStage {
4392                    module: shader_module.raw(),
4393                    entry_point: &fragment_entry_point_name,
4394                    constants: &fragment_state.stage.constants,
4395                    zero_initialize_workgroup_memory: fragment_state
4396                        .stage
4397                        .zero_initialize_workgroup_memory,
4398                })
4399            }
4400            None => None,
4401        };
4402
4403        if !pipeline_expects_dual_source_blending && shader_expects_dual_source_blending {
4404            return Err(
4405                pipeline::CreateRenderPipelineError::ShaderExpectsPipelineToUseDualSourceBlending,
4406            );
4407        }
4408        if pipeline_expects_dual_source_blending && !shader_expects_dual_source_blending {
4409            return Err(
4410                pipeline::CreateRenderPipelineError::PipelineExpectsShaderToUseDualSourceBlending,
4411            );
4412        }
4413
4414        if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
4415            for (i, output) in io.iter() {
4416                match color_targets.get(*i as usize) {
4417                    Some(Some(state)) => {
4418                        validation::check_texture_format(state.format, &output.ty).map_err(
4419                            |pipeline| {
4420                                pipeline::CreateRenderPipelineError::ColorState(
4421                                    *i as u8,
4422                                    pipeline::ColorStateError::IncompatibleFormat {
4423                                        pipeline,
4424                                        shader: output.ty,
4425                                    },
4426                                )
4427                            },
4428                        )?;
4429                    }
4430                    _ => {
4431                        log::warn!(
4432                            "The fragment stage {:?} output @location({}) values are ignored",
4433                            fragment_stage
4434                                .as_ref()
4435                                .map_or("", |stage| stage.entry_point),
4436                            i
4437                        );
4438                    }
4439                }
4440            }
4441        }
4442        let last_stage = match desc.fragment {
4443            Some(_) => wgt::ShaderStages::FRAGMENT,
4444            None => wgt::ShaderStages::VERTEX,
4445        };
4446        if is_auto_layout && !validated_stages.contains(last_stage) {
4447            return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
4448        }
4449
4450        let pipeline_layout = match binding_layout_source {
4451            validation::BindingLayoutSource::Provided(_) => {
4452                drop(binding_layout_source);
4453                pipeline_layout.unwrap()
4454            }
4455            validation::BindingLayoutSource::Derived(entries) => {
4456                self.derive_pipeline_layout(entries)?
4457            }
4458        };
4459
4460        // Multiview is only supported if the feature is enabled
4461        if let Some(mv_mask) = desc.multiview_mask {
4462            self.require_features(wgt::Features::MULTIVIEW)?;
4463            if !(mv_mask.get() + 1).is_power_of_two() {
4464                self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
4465            }
4466        }
4467
4468        if !self
4469            .downlevel
4470            .flags
4471            .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
4472        {
4473            for (binding, size) in shader_binding_sizes.iter() {
4474                if size.get() % 16 != 0 {
4475                    return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
4476                        binding: binding.binding,
4477                        group: binding.group,
4478                        size: size.get(),
4479                    });
4480                }
4481            }
4482        }
4483
4484        let late_sized_buffer_groups =
4485            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
4486
4487        let cache = match desc.cache {
4488            Some(cache) => {
4489                cache.same_device(self)?;
4490                Some(cache)
4491            }
4492            None => None,
4493        };
4494
4495        let is_mesh = mesh_stage.is_some();
4496        let raw = {
4497            let pipeline_desc = hal::RenderPipelineDescriptor {
4498                label: desc.label.to_hal(self.instance_flags),
4499                layout: pipeline_layout.raw(),
4500                vertex_processor: match vertex_stage {
4501                    Some(vertex_stage) => hal::VertexProcessor::Standard {
4502                        vertex_buffers: &vertex_buffers,
4503                        vertex_stage,
4504                    },
4505                    None => hal::VertexProcessor::Mesh {
4506                        task_stage,
4507                        mesh_stage: mesh_stage.unwrap(),
4508                    },
4509                },
4510                primitive: desc.primitive,
4511                depth_stencil: desc.depth_stencil.clone(),
4512                multisample: desc.multisample,
4513                fragment_stage,
4514                color_targets,
4515                multiview_mask: desc.multiview_mask,
4516                cache: cache.as_ref().map(|it| it.raw()),
4517            };
4518            unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
4519                |err| match err {
4520                    hal::PipelineError::Device(error) => {
4521                        pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))
4522                    }
4523                    hal::PipelineError::Linkage(stage, msg) => {
4524                        pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
4525                    }
4526                    hal::PipelineError::EntryPoint(stage) => {
4527                        pipeline::CreateRenderPipelineError::Internal {
4528                            stage: hal::auxil::map_naga_stage(stage),
4529                            error: ENTRYPOINT_FAILURE_ERROR.to_string(),
4530                        }
4531                    }
4532                    hal::PipelineError::PipelineConstants(stage, error) => {
4533                        pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }
4534                    }
4535                },
4536            )?
4537        };
4538
4539        let pass_context = RenderPassContext {
4540            attachments: AttachmentData {
4541                colors: color_targets
4542                    .iter()
4543                    .map(|state| state.as_ref().map(|s| s.format))
4544                    .collect(),
4545                resolves: ArrayVec::new(),
4546                depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
4547            },
4548            sample_count: samples,
4549            multiview_mask: desc.multiview_mask,
4550        };
4551
4552        let mut flags = pipeline::PipelineFlags::empty();
4553        for state in color_targets.iter().filter_map(|s| s.as_ref()) {
4554            if let Some(ref bs) = state.blend {
4555                if bs.color.uses_constant() | bs.alpha.uses_constant() {
4556                    flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
4557                }
4558            }
4559        }
4560        if let Some(ds) = depth_stencil_state.as_ref() {
4561            if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
4562                flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
4563            }
4564            if !ds.is_depth_read_only() {
4565                flags |= pipeline::PipelineFlags::WRITES_DEPTH;
4566            }
4567            if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
4568                flags |= pipeline::PipelineFlags::WRITES_STENCIL;
4569            }
4570        }
4571        let shader_modules = {
4572            let mut shader_modules = ArrayVec::new();
4573            match desc.vertex {
4574                pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
4575                    shader_modules.push(vertex.stage.module)
4576                }
4577                pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
4578                    if let Some(task) = task {
4579                        shader_modules.push(task.stage.module);
4580                    }
4581                    shader_modules.push(mesh.stage.module);
4582                }
4583            }
4584            shader_modules.extend(desc.fragment.map(|f| f.stage.module));
4585            shader_modules
4586        };
4587
4588        let pipeline = pipeline::RenderPipeline {
4589            raw: ManuallyDrop::new(raw),
4590            layout: pipeline_layout,
4591            device: self.clone(),
4592            pass_context,
4593            _shader_modules: shader_modules,
4594            flags,
4595            strip_index_format: desc.primitive.strip_index_format,
4596            vertex_steps,
4597            late_sized_buffer_groups,
4598            label: desc.label.to_string(),
4599            tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),
4600            is_mesh,
4601        };
4602
4603        let pipeline = Arc::new(pipeline);
4604
4605        if is_auto_layout {
4606            for bgl in pipeline.layout.bind_group_layouts.iter() {
4607                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
4608                let _ = bgl
4609                    .exclusive_pipeline
4610                    .set(binding_model::ExclusivePipeline::Render(Arc::downgrade(
4611                        &pipeline,
4612                    )));
4613            }
4614        }
4615
4616        Ok(pipeline)
4617    }
4618
4619    /// # Safety
4620    /// The `data` field on `desc` must have previously been returned from [`crate::global::Global::pipeline_cache_get_data`]
4621    pub unsafe fn create_pipeline_cache(
4622        self: &Arc<Self>,
4623        desc: &pipeline::PipelineCacheDescriptor,
4624    ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {
4625        use crate::pipeline_cache;
4626
4627        self.check_is_valid()?;
4628
4629        self.require_features(wgt::Features::PIPELINE_CACHE)?;
4630        let data = if let Some((data, validation_key)) = desc
4631            .data
4632            .as_ref()
4633            .zip(self.raw().pipeline_cache_validation_key())
4634        {
4635            let data = pipeline_cache::validate_pipeline_cache(
4636                data,
4637                &self.adapter.raw.info,
4638                validation_key,
4639            );
4640            match data {
4641                Ok(data) => Some(data),
4642                Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),
4643                // If the error was unavoidable and we are asked to fallback, do so
4644                Err(_) => None,
4645            }
4646        } else {
4647            None
4648        };
4649        let cache_desc = hal::PipelineCacheDescriptor {
4650            data,
4651            label: desc.label.to_hal(self.instance_flags),
4652        };
4653        let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {
4654            Ok(raw) => raw,
4655            Err(e) => match e {
4656                hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),
4657            },
4658        };
4659        let cache = pipeline::PipelineCache {
4660            device: self.clone(),
4661            label: desc.label.to_string(),
4662            // This would be none in the error condition, which we don't implement yet
4663            raw: ManuallyDrop::new(raw),
4664        };
4665
4666        let cache = Arc::new(cache);
4667
4668        Ok(cache)
4669    }
4670
4671    fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {
4672        // Variant of adapter.get_texture_format_features that takes device features into account
4673        use wgt::TextureFormatFeatureFlags as tfsc;
4674        let mut format_features = self.adapter.get_texture_format_features(format);
4675        if (format == TextureFormat::R32Float
4676            || format == TextureFormat::Rg32Float
4677            || format == TextureFormat::Rgba32Float)
4678            && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
4679        {
4680            format_features.flags.set(tfsc::FILTERABLE, false);
4681        }
4682        format_features
4683    }
4684
4685    fn describe_format_features(
4686        &self,
4687        format: TextureFormat,
4688    ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
4689        self.require_features(format.required_features())?;
4690
4691        let using_device_features = self
4692            .features
4693            .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
4694        // If we're running downlevel, we need to manually ask the backend what
4695        // we can use as we can't trust WebGPU.
4696        let downlevel = !self
4697            .downlevel
4698            .flags
4699            .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
4700
4701        if using_device_features || downlevel {
4702            Ok(self.get_texture_format_features(format))
4703        } else {
4704            Ok(format.guaranteed_format_features(self.features))
4705        }
4706    }
4707
4708    #[cfg(feature = "replay")]
4709    pub(crate) fn wait_for_submit(
4710        &self,
4711        submission_index: crate::SubmissionIndex,
4712    ) -> Result<(), DeviceError> {
4713        let fence = self.fence.read();
4714        let last_done_index = unsafe { self.raw().get_fence_value(fence.as_ref()) }
4715            .map_err(|e| self.handle_hal_error(e))?;
4716        if last_done_index < submission_index {
4717            unsafe { self.raw().wait(fence.as_ref(), submission_index, None) }
4718                .map_err(|e| self.handle_hal_error(e))?;
4719            drop(fence);
4720            if let Some(queue) = self.get_queue() {
4721                let closures = queue.lock_life().triage_submissions(submission_index);
4722                assert!(
4723                    closures.is_empty(),
4724                    "wait_for_submit is not expected to work with closures"
4725                );
4726            }
4727        }
4728        Ok(())
4729    }
4730
4731    pub fn create_query_set(
4732        self: &Arc<Self>,
4733        desc: &resource::QuerySetDescriptor,
4734    ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {
4735        use resource::CreateQuerySetError as Error;
4736
4737        self.check_is_valid()?;
4738
4739        match desc.ty {
4740            wgt::QueryType::Occlusion => {}
4741            wgt::QueryType::Timestamp => {
4742                self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
4743            }
4744            wgt::QueryType::PipelineStatistics(..) => {
4745                self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
4746            }
4747        }
4748
4749        if desc.count == 0 {
4750            return Err(Error::ZeroCount);
4751        }
4752
4753        if desc.count > wgt::QUERY_SET_MAX_QUERIES {
4754            return Err(Error::TooManyQueries {
4755                count: desc.count,
4756                maximum: wgt::QUERY_SET_MAX_QUERIES,
4757            });
4758        }
4759
4760        let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
4761
4762        let raw = unsafe { self.raw().create_query_set(&hal_desc) }
4763            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
4764
4765        let query_set = QuerySet {
4766            raw: ManuallyDrop::new(raw),
4767            device: self.clone(),
4768            label: desc.label.to_string(),
4769            tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),
4770            desc: desc.map_label(|_| ()),
4771        };
4772
4773        let query_set = Arc::new(query_set);
4774
4775        Ok(query_set)
4776    }
4777
4778    pub fn configure_surface(
4779        self: &Arc<Self>,
4780        surface: &crate::instance::Surface,
4781        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
4782    ) -> Option<present::ConfigureSurfaceError> {
4783        use present::ConfigureSurfaceError as E;
4784        profiling::scope!("surface_configure");
4785
4786        fn validate_surface_configuration(
4787            config: &mut hal::SurfaceConfiguration,
4788            caps: &hal::SurfaceCapabilities,
4789            max_texture_dimension_2d: u32,
4790        ) -> Result<(), E> {
4791            let width = config.extent.width;
4792            let height = config.extent.height;
4793
4794            if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
4795                return Err(E::TooLarge {
4796                    width,
4797                    height,
4798                    max_texture_dimension_2d,
4799                });
4800            }
4801
4802            if !caps.present_modes.contains(&config.present_mode) {
4803                // Automatic present mode checks.
4804                //
4805                // The "Automatic" modes are never supported by the backends.
4806                let fallbacks = match config.present_mode {
4807                    wgt::PresentMode::AutoVsync => {
4808                        &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]
4809                    }
4810                    // Always end in FIFO to make sure it's always supported
4811                    wgt::PresentMode::AutoNoVsync => &[
4812                        wgt::PresentMode::Immediate,
4813                        wgt::PresentMode::Mailbox,
4814                        wgt::PresentMode::Fifo,
4815                    ][..],
4816                    _ => {
4817                        return Err(E::UnsupportedPresentMode {
4818                            requested: config.present_mode,
4819                            available: caps.present_modes.clone(),
4820                        });
4821                    }
4822                };
4823
4824                let new_mode = fallbacks
4825                    .iter()
4826                    .copied()
4827                    .find(|fallback| caps.present_modes.contains(fallback))
4828                    .unwrap_or_else(|| {
4829                        unreachable!(
4830                            "Fallback system failed to choose present mode. \
4831                            This is a bug. Mode: {:?}, Options: {:?}",
4832                            config.present_mode, &caps.present_modes
4833                        );
4834                    });
4835
4836                api_log!(
4837                    "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
4838                    config.present_mode
4839                );
4840                config.present_mode = new_mode;
4841            }
4842            if !caps.formats.contains(&config.format) {
4843                return Err(E::UnsupportedFormat {
4844                    requested: config.format,
4845                    available: caps.formats.clone(),
4846                });
4847            }
4848            if !caps
4849                .composite_alpha_modes
4850                .contains(&config.composite_alpha_mode)
4851            {
4852                let new_alpha_mode = 'alpha: {
4853                    // Automatic alpha mode checks.
4854                    let fallbacks = match config.composite_alpha_mode {
4855                        wgt::CompositeAlphaMode::Auto => &[
4856                            wgt::CompositeAlphaMode::Opaque,
4857                            wgt::CompositeAlphaMode::Inherit,
4858                        ][..],
4859                        _ => {
4860                            return Err(E::UnsupportedAlphaMode {
4861                                requested: config.composite_alpha_mode,
4862                                available: caps.composite_alpha_modes.clone(),
4863                            });
4864                        }
4865                    };
4866
4867                    for &fallback in fallbacks {
4868                        if caps.composite_alpha_modes.contains(&fallback) {
4869                            break 'alpha fallback;
4870                        }
4871                    }
4872
4873                    unreachable!(
4874                        "Fallback system failed to choose alpha mode. This is a bug. \
4875                                  AlphaMode: {:?}, Options: {:?}",
4876                        config.composite_alpha_mode, &caps.composite_alpha_modes
4877                    );
4878                };
4879
4880                api_log!(
4881                    "Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
4882                    config.composite_alpha_mode
4883                );
4884                config.composite_alpha_mode = new_alpha_mode;
4885            }
4886            if !caps.usage.contains(config.usage) {
4887                return Err(E::UnsupportedUsage {
4888                    requested: config.usage,
4889                    available: caps.usage,
4890                });
4891            }
4892            if width == 0 || height == 0 {
4893                return Err(E::ZeroArea);
4894            }
4895            Ok(())
4896        }
4897
4898        log::debug!("configuring surface with {config:?}");
4899
4900        let error = 'error: {
4901            // User callbacks must not be called while we are holding locks.
4902            let user_callbacks;
4903            {
4904                if let Err(e) = self.check_is_valid() {
4905                    break 'error e.into();
4906                }
4907
4908                let caps = match surface.get_capabilities(&self.adapter) {
4909                    Ok(caps) => caps,
4910                    Err(_) => break 'error E::UnsupportedQueueFamily,
4911                };
4912
4913                let mut hal_view_formats = Vec::new();
4914                for format in config.view_formats.iter() {
4915                    if *format == config.format {
4916                        continue;
4917                    }
4918                    if !caps.formats.contains(&config.format) {
4919                        break 'error E::UnsupportedFormat {
4920                            requested: config.format,
4921                            available: caps.formats,
4922                        };
4923                    }
4924                    if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
4925                        break 'error E::InvalidViewFormat(*format, config.format);
4926                    }
4927                    hal_view_formats.push(*format);
4928                }
4929
4930                if !hal_view_formats.is_empty() {
4931                    if let Err(missing_flag) =
4932                        self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
4933                    {
4934                        break 'error E::MissingDownlevelFlags(missing_flag);
4935                    }
4936                }
4937
4938                let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
4939                    *caps.maximum_frame_latency.start(),
4940                    *caps.maximum_frame_latency.end(),
4941                );
4942                let mut hal_config = hal::SurfaceConfiguration {
4943                    maximum_frame_latency,
4944                    present_mode: config.present_mode,
4945                    composite_alpha_mode: config.alpha_mode,
4946                    format: config.format,
4947                    extent: wgt::Extent3d {
4948                        width: config.width,
4949                        height: config.height,
4950                        depth_or_array_layers: 1,
4951                    },
4952                    usage: conv::map_texture_usage(
4953                        config.usage,
4954                        hal::FormatAspects::COLOR,
4955                        wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
4956                            | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
4957                            | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
4958                    ),
4959                    view_formats: hal_view_formats,
4960                };
4961
4962                if let Err(error) = validate_surface_configuration(
4963                    &mut hal_config,
4964                    &caps,
4965                    self.limits.max_texture_dimension_2d,
4966                ) {
4967                    break 'error error;
4968                }
4969
4970                // Wait for all work to finish before configuring the surface.
4971                let snatch_guard = self.snatchable_lock.read();
4972                let fence = self.fence.read();
4973
4974                let maintain_result;
4975                (user_callbacks, maintain_result) =
4976                    self.maintain(fence, wgt::PollType::wait_indefinitely(), snatch_guard);
4977
4978                match maintain_result {
4979                    // We're happy
4980                    Ok(wgt::PollStatus::QueueEmpty) => {}
4981                    Ok(wgt::PollStatus::WaitSucceeded) => {
4982                        // After the wait, the queue should be empty. It can only be non-empty
4983                        // if another thread is submitting at the same time.
4984                        break 'error E::GpuWaitTimeout;
4985                    }
4986                    Ok(wgt::PollStatus::Poll) => {
4987                        unreachable!("Cannot get a Poll result from a Wait action.")
4988                    }
4989                    Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
4990                        // On wasm, you cannot actually successfully wait for the surface.
4991                        // However WebGL does not actually require you do this, so ignoring
4992                        // the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363
4993                    }
4994                    Err(e) => {
4995                        break 'error e.into();
4996                    }
4997                }
4998
4999                // All textures must be destroyed before the surface can be re-configured.
5000                if let Some(present) = surface.presentation.lock().take() {
5001                    if present.acquired_texture.is_some() {
5002                        break 'error E::PreviousOutputExists;
5003                    }
5004                }
5005
5006                // TODO: Texture views may still be alive that point to the texture.
5007                // this will allow the user to render to the surface texture, long after
5008                // it has been removed.
5009                //
5010                // https://github.com/gfx-rs/wgpu/issues/4105
5011
5012                let surface_raw = surface.raw(self.backend()).unwrap();
5013                match unsafe { surface_raw.configure(self.raw(), &hal_config) } {
5014                    Ok(()) => (),
5015                    Err(error) => {
5016                        break 'error match error {
5017                            hal::SurfaceError::Outdated | hal::SurfaceError::Lost => {
5018                                E::InvalidSurface
5019                            }
5020                            hal::SurfaceError::Device(error) => {
5021                                E::Device(self.handle_hal_error(error))
5022                            }
5023                            hal::SurfaceError::Other(message) => {
5024                                log::error!("surface configuration failed: {message}");
5025                                E::InvalidSurface
5026                            }
5027                        }
5028                    }
5029                }
5030
5031                let mut presentation = surface.presentation.lock();
5032                *presentation = Some(present::Presentation {
5033                    device: Arc::clone(self),
5034                    config: config.clone(),
5035                    acquired_texture: None,
5036                });
5037            }
5038
5039            user_callbacks.fire();
5040            return None;
5041        };
5042
5043        Some(error)
5044    }
5045
5046    fn lose(&self, message: &str) {
5047        // Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device.
5048
5049        // Mark the device explicitly as invalid. This is checked in various
5050        // places to prevent new work from being submitted.
5051        self.valid.store(false, Ordering::Release);
5052
5053        // 1) Resolve the GPUDevice device.lost promise.
5054        if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
5055            device_lost_closure(DeviceLostReason::Unknown, message.to_string());
5056        }
5057
5058        // 2) Complete any outstanding mapAsync() steps.
5059        // 3) Complete any outstanding onSubmittedWorkDone() steps.
5060
5061        // These parts are passively accomplished by setting valid to false,
5062        // since that will prevent any new work from being added to the queues.
5063        // Future calls to poll_devices will continue to check the work queues
5064        // until they are cleared, and then drop the device.
5065    }
5066
5067    fn release_gpu_resources(&self) {
5068        // This is called when the device is lost, which makes every associated
5069        // resource invalid and unusable. This is an opportunity to release all of
5070        // the underlying gpu resources, even though the objects remain visible to
5071        // the user agent. We purge this memory naturally when resources have been
5072        // moved into the appropriate buckets, so this function just needs to
5073        // initiate movement into those buckets, and it can do that by calling
5074        // "destroy" on all the resources we know about.
5075
5076        // During these iterations, we discard all errors. We don't care!
5077        let trackers = self.trackers.lock();
5078        for buffer in trackers.buffers.used_resources() {
5079            if let Some(buffer) = Weak::upgrade(buffer) {
5080                buffer.destroy();
5081            }
5082        }
5083        for texture in trackers.textures.used_resources() {
5084            if let Some(texture) = Weak::upgrade(texture) {
5085                texture.destroy();
5086            }
5087        }
5088    }
5089
5090    pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {
5091        UsageScope::new_pooled(&self.usage_scopes, &self.tracker_indices)
5092    }
5093
5094    pub fn get_hal_counters(&self) -> wgt::HalCounters {
5095        self.raw().get_internal_counters()
5096    }
5097
5098    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
5099        self.raw().generate_allocator_report()
5100    }
5101}
5102
5103crate::impl_resource_type!(Device);
5104crate::impl_labeled!(Device);
5105crate::impl_storage_item!(Device);