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