Skip to main content

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