wgpu_core/device/
resource.rs

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