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    /// A module of GLSL templates, one per entry point, for the GL backend.
2601    ///
2602    /// # Safety
2603    ///
2604    /// No naga checks the templates: a driver gets their GLSL as written.
2605    /// On WebGL the browser checks it.
2606    pub unsafe fn create_shader_module_glsl_templates(
2607        self: &Arc<Self>,
2608        label: &crate::Label<'_>,
2609        templates: &'static [hal::GlslTemplate],
2610    ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
2611        self.check_is_valid()?;
2612        if self.backend() != wgt::Backend::Gl {
2613            return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend);
2614        }
2615        let hal_desc = hal::ShaderModuleDescriptor {
2616            label: label.to_hal(self.instance_flags),
2617            runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),
2618        };
2619        let raw = unsafe {
2620            self.raw()
2621                .create_shader_module(&hal_desc, hal::ShaderInput::GlslTemplates(templates))
2622        }
2623        .map_err(|error| match error {
2624            hal::ShaderError::Device(error) => {
2625                pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
2626            }
2627            hal::ShaderError::Compilation(ref msg) => {
2628                log::error!("Shader error: {msg}");
2629                pipeline::CreateShaderModuleError::Generation
2630            }
2631        })?;
2632        Ok(Arc::new(pipeline::ShaderModule {
2633            raw: ManuallyDrop::new(raw),
2634            device: self.clone(),
2635            interface: ShaderMetaData::Passthrough(PassthroughInterface {
2636                entry_point_names: templates
2637                    .iter()
2638                    .map(|template| template.entry_point.to_string())
2639                    .collect(),
2640            }),
2641            label: label.to_string(),
2642        }))
2643    }
2644
2645    pub(crate) fn create_command_encoder(
2646        self: &Arc<Self>,
2647        label: &crate::Label,
2648    ) -> Result<Arc<command::CommandEncoder>, DeviceError> {
2649        self.check_is_valid()?;
2650
2651        let queue = self.get_queue().unwrap();
2652
2653        let encoder = self
2654            .command_allocator
2655            .acquire_encoder(self.raw(), queue.raw())
2656            .map_err(|e| self.handle_hal_error(e))?;
2657
2658        let cmd_enc = command::CommandEncoder::new(encoder, self, label);
2659
2660        let cmd_enc = Arc::new(cmd_enc);
2661
2662        Ok(cmd_enc)
2663    }
2664
2665    /// Generate information about late-validated buffer bindings for pipelines.
2666    //TODO: should this be combined with `get_introspection_bind_group_layouts` in some way?
2667    fn make_late_sized_buffer_groups(
2668        shader_binding_sizes: &FastHashMap<naga::ResourceBinding, wgt::BufferSize>,
2669        layout: &binding_model::PipelineLayout,
2670    ) -> ArrayVec<pipeline::LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }> {
2671        // Given the shader-required binding sizes and the pipeline layout,
2672        // return the filtered list of them in the layout order,
2673        // removing those with given `min_binding_size`.
2674        layout
2675            .bind_group_layouts
2676            .iter()
2677            .enumerate()
2678            .map(|(group_index, bgl)| {
2679                let Some(bgl) = bgl else {
2680                    return pipeline::LateSizedBufferGroup::default();
2681                };
2682
2683                let shader_sizes = bgl
2684                    .entries
2685                    .values()
2686                    .filter_map(|entry| match entry.ty {
2687                        wgt::BindingType::Buffer {
2688                            min_binding_size: None,
2689                            ..
2690                        } => {
2691                            let rb = naga::ResourceBinding {
2692                                group: group_index as u32,
2693                                binding: entry.binding,
2694                            };
2695                            let shader_size =
2696                                shader_binding_sizes.get(&rb).map_or(0, |nz| nz.get());
2697                            Some(shader_size)
2698                        }
2699                        _ => None,
2700                    })
2701                    .collect();
2702                pipeline::LateSizedBufferGroup { shader_sizes }
2703            })
2704            .collect()
2705    }
2706
2707    pub fn create_bind_group_layout(
2708        self: &Arc<Self>,
2709        desc: &binding_model::BindGroupLayoutDescriptor,
2710    ) -> (Arc<BindGroupLayout>, Option<CreateBindGroupLayoutError>) {
2711        let (bgl, error) = match self.create_bind_group_layout_inner(desc) {
2712            Ok(layout) => (layout, None),
2713            Err(e) => (
2714                BindGroupLayout::invalid(self, desc.label.to_string()),
2715                Some(e),
2716            ),
2717        };
2718        #[cfg(feature = "trace")]
2719        if let Some(ref mut trace) = *self.trace.lock() {
2720            use crate::device::trace::IntoTrace;
2721
2722            trace.add(trace::Action::CreateBindGroupLayout(
2723                bgl.to_trace(),
2724                desc.clone(),
2725            ));
2726        }
2727        (bgl, error)
2728    }
2729
2730    fn create_bind_group_layout_inner(
2731        self: &Arc<Device>,
2732        desc: &binding_model::BindGroupLayoutDescriptor,
2733    ) -> Result<Arc<BindGroupLayout>, CreateBindGroupLayoutError> {
2734        self.check_is_valid()?;
2735
2736        let entry_map = bgl::EntryMap::from_entries(&desc.entries)?;
2737
2738        let bgl_result = self.bgl_pool.get_or_init(entry_map, |entry_map| {
2739            let bgl =
2740                self.create_bind_group_layout_impl(&desc.label, entry_map, bgl::Origin::Pool)?;
2741            bgl.exclusive_pipeline
2742                .set(binding_model::ExclusivePipeline::None)
2743                .unwrap();
2744            Ok(bgl)
2745        });
2746
2747        match bgl_result {
2748            Ok(layout) => Ok(layout),
2749            Err(e) => Err(e),
2750        }
2751    }
2752
2753    fn create_bind_group_layout_impl(
2754        self: &Arc<Self>,
2755        label: &crate::Label,
2756        entry_map: bgl::EntryMap,
2757        origin: bgl::Origin,
2758    ) -> Result<Arc<BindGroupLayout>, CreateBindGroupLayoutError> {
2759        #[derive(PartialEq)]
2760        enum WritableStorage {
2761            Yes,
2762            No,
2763        }
2764
2765        for entry in entry_map.values() {
2766            if entry.binding >= self.limits.max_bindings_per_bind_group {
2767                return Err(CreateBindGroupLayoutError::InvalidBindingIndex {
2768                    binding: entry.binding,
2769                    maximum: self.limits.max_bindings_per_bind_group,
2770                });
2771            }
2772
2773            use wgt::BindingType as Bt;
2774
2775            let mut required_features = wgt::Features::empty();
2776            let mut required_downlevel_flags = wgt::DownlevelFlags::empty();
2777            let (array_feature, writable_storage) = match entry.ty {
2778                Bt::Buffer {
2779                    ty: wgt::BufferBindingType::Uniform,
2780                    has_dynamic_offset: false,
2781                    min_binding_size: _,
2782                } => (
2783                    Some(wgt::Features::BUFFER_BINDING_ARRAY),
2784                    WritableStorage::No,
2785                ),
2786                Bt::Buffer {
2787                    ty: wgt::BufferBindingType::Uniform,
2788                    has_dynamic_offset: true,
2789                    min_binding_size: _,
2790                } => (
2791                    Some(wgt::Features::BUFFER_BINDING_ARRAY),
2792                    WritableStorage::No,
2793                ),
2794                Bt::Buffer {
2795                    ty: wgt::BufferBindingType::Storage { read_only },
2796                    ..
2797                } => (
2798                    Some(
2799                        wgt::Features::BUFFER_BINDING_ARRAY
2800                            | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
2801                    ),
2802                    match read_only {
2803                        true => WritableStorage::No,
2804                        false => WritableStorage::Yes,
2805                    },
2806                ),
2807                Bt::Sampler { .. } => (
2808                    Some(wgt::Features::TEXTURE_BINDING_ARRAY),
2809                    WritableStorage::No,
2810                ),
2811                Bt::Texture {
2812                    multisampled: true,
2813                    sample_type: TextureSampleType::Float { filterable: true },
2814                    ..
2815                } => {
2816                    return Err(CreateBindGroupLayoutError::Entry {
2817                        binding: entry.binding,
2818                        error:
2819                            BindGroupLayoutEntryError::SampleTypeFloatFilterableBindingMultisampled,
2820                    });
2821                }
2822                Bt::Texture {
2823                    multisampled,
2824                    view_dimension,
2825                    ..
2826                } => {
2827                    if multisampled && view_dimension != TextureViewDimension::D2 {
2828                        return Err(CreateBindGroupLayoutError::Entry {
2829                            binding: entry.binding,
2830                            error: BindGroupLayoutEntryError::Non2DMultisampled(view_dimension),
2831                        });
2832                    }
2833
2834                    (
2835                        Some(wgt::Features::TEXTURE_BINDING_ARRAY),
2836                        WritableStorage::No,
2837                    )
2838                }
2839                Bt::StorageTexture {
2840                    access,
2841                    view_dimension,
2842                    format,
2843                } => {
2844                    use wgt::{StorageTextureAccess as Access, TextureFormatFeatureFlags as Flags};
2845
2846                    match view_dimension {
2847                        TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
2848                            return Err(CreateBindGroupLayoutError::Entry {
2849                                binding: entry.binding,
2850                                error: BindGroupLayoutEntryError::StorageTextureCube,
2851                            })
2852                        }
2853                        _ => (),
2854                    }
2855                    match access {
2856                        wgt::StorageTextureAccess::Atomic
2857                            if !self.features.contains(wgt::Features::TEXTURE_ATOMIC) =>
2858                        {
2859                            return Err(CreateBindGroupLayoutError::Entry {
2860                                binding: entry.binding,
2861                                error: BindGroupLayoutEntryError::StorageTextureAtomic,
2862                            });
2863                        }
2864                        _ => (),
2865                    }
2866
2867                    let format_features =
2868                        self.describe_format_features(format).map_err(|error| {
2869                            CreateBindGroupLayoutError::Entry {
2870                                binding: entry.binding,
2871                                error: BindGroupLayoutEntryError::MissingFeatures(error),
2872                            }
2873                        })?;
2874
2875                    let required_feature_flag = match access {
2876                        Access::WriteOnly => Flags::STORAGE_WRITE_ONLY,
2877                        Access::ReadOnly => Flags::STORAGE_READ_ONLY,
2878                        Access::ReadWrite => Flags::STORAGE_READ_WRITE,
2879                        Access::Atomic => Flags::STORAGE_ATOMIC,
2880                    };
2881
2882                    if !format_features.flags.contains(required_feature_flag) {
2883                        return Err(
2884                            CreateBindGroupLayoutError::UnsupportedStorageTextureAccess {
2885                                binding: entry.binding,
2886                                access,
2887                                format,
2888                            },
2889                        );
2890                    }
2891
2892                    (
2893                        Some(
2894                            wgt::Features::TEXTURE_BINDING_ARRAY
2895                                | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
2896                        ),
2897                        match access {
2898                            wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes,
2899                            wgt::StorageTextureAccess::ReadOnly => WritableStorage::No,
2900                            wgt::StorageTextureAccess::ReadWrite => WritableStorage::Yes,
2901                            wgt::StorageTextureAccess::Atomic => {
2902                                required_features |= wgt::Features::TEXTURE_ATOMIC;
2903                                WritableStorage::Yes
2904                            }
2905                        },
2906                    )
2907                }
2908                Bt::AccelerationStructure { vertex_return } => {
2909                    self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)
2910                        .map_err(|e| CreateBindGroupLayoutError::Entry {
2911                            binding: entry.binding,
2912                            error: e.into(),
2913                        })?;
2914                    if vertex_return {
2915                        self.require_features(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN)
2916                            .map_err(|e| CreateBindGroupLayoutError::Entry {
2917                                binding: entry.binding,
2918                                error: e.into(),
2919                            })?;
2920                    }
2921                    (
2922                        Some(wgt::Features::ACCELERATION_STRUCTURE_BINDING_ARRAY),
2923                        WritableStorage::No,
2924                    )
2925                }
2926                Bt::ExternalTexture => {
2927                    self.require_features(wgt::Features::EXTERNAL_TEXTURE)
2928                        .map_err(|e| CreateBindGroupLayoutError::Entry {
2929                            binding: entry.binding,
2930                            error: e.into(),
2931                        })?;
2932                    (None, WritableStorage::No)
2933                }
2934            };
2935
2936            // Validate the count parameter
2937            if entry.count.is_some() {
2938                required_features |= array_feature
2939                    .ok_or(BindGroupLayoutEntryError::ArrayUnsupported)
2940                    .map_err(|error| CreateBindGroupLayoutError::Entry {
2941                        binding: entry.binding,
2942                        error,
2943                    })?;
2944            }
2945
2946            if entry.visibility.contains_unknown_bits() {
2947                return Err(CreateBindGroupLayoutError::InvalidVisibility(
2948                    entry.visibility,
2949                ));
2950            }
2951
2952            if entry.visibility.contains(wgt::ShaderStages::VERTEX) {
2953                if writable_storage == WritableStorage::Yes {
2954                    required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
2955                }
2956                if let Bt::Buffer {
2957                    ty: wgt::BufferBindingType::Storage { .. },
2958                    ..
2959                } = entry.ty
2960                {
2961                    required_downlevel_flags |= wgt::DownlevelFlags::VERTEX_STORAGE;
2962                }
2963            }
2964            if writable_storage == WritableStorage::Yes
2965                && entry.visibility.contains(wgt::ShaderStages::FRAGMENT)
2966            {
2967                required_downlevel_flags |= wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE;
2968            }
2969
2970            self.require_features(required_features)
2971                .map_err(BindGroupLayoutEntryError::MissingFeatures)
2972                .map_err(|error| CreateBindGroupLayoutError::Entry {
2973                    binding: entry.binding,
2974                    error,
2975                })?;
2976            self.require_downlevel_flags(required_downlevel_flags)
2977                .map_err(BindGroupLayoutEntryError::MissingDownlevelFlags)
2978                .map_err(|error| CreateBindGroupLayoutError::Entry {
2979                    binding: entry.binding,
2980                    error,
2981                })?;
2982        }
2983
2984        let bgl_flags = conv::bind_group_layout_flags(self.features);
2985
2986        let hal_bindings = entry_map.values().copied().collect::<Vec<_>>();
2987        let hal_desc = hal::BindGroupLayoutDescriptor {
2988            label: label.to_hal(self.instance_flags),
2989            flags: bgl_flags,
2990            entries: &hal_bindings,
2991        };
2992
2993        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
2994        for entry in entry_map.values() {
2995            count_validator.add_binding(entry);
2996        }
2997        // If a single bind group layout violates limits, the pipeline layout is
2998        // definitely going to violate limits too, lets catch it now.
2999        count_validator
3000            .validate(&self.limits, self.instance_flags)
3001            .map_err(CreateBindGroupLayoutError::TooManyBindings)?;
3002
3003        // Validate that binding arrays don't conflict with dynamic offsets.
3004        count_validator.validate_binding_arrays()?;
3005
3006        let raw = unsafe { self.raw().create_bind_group_layout(&hal_desc) }
3007            .map_err(|e| self.handle_hal_error(e))?;
3008
3009        let bgl = BindGroupLayout {
3010            state: ResourceState::Valid(BindGroupLayoutState {
3011                raw: binding_model::RawBindGroupLayout::Owning(ManuallyDrop::new(raw)),
3012                origin,
3013                binding_count_validator: count_validator,
3014            }),
3015            device: self.clone(),
3016            entries: entry_map,
3017            exclusive_pipeline: OnceCellOrLock::new(),
3018            label: label.to_string(),
3019        };
3020
3021        let bgl = Arc::new(bgl);
3022
3023        Ok(bgl)
3024    }
3025
3026    fn create_buffer_binding<'a>(
3027        &self,
3028        bb: &'a binding_model::ResolvedBufferBinding,
3029        binding: u32,
3030        decl: &wgt::BindGroupLayoutEntry,
3031        buffer_init_actions: &mut Vec<BufferInitTrackerAction>,
3032        dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
3033        late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
3034        used: &mut BindGroupStates,
3035        snatch_guard: &'a SnatchGuard<'a>,
3036    ) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, CreateBindGroupError> {
3037        use crate::binding_model::CreateBindGroupError as Error;
3038
3039        let (binding_ty, dynamic, min_size) = match decl.ty {
3040            wgt::BindingType::Buffer {
3041                ty,
3042                has_dynamic_offset,
3043                min_binding_size,
3044            } => (ty, has_dynamic_offset, min_binding_size),
3045            _ => {
3046                return Err(Error::WrongBindingType {
3047                    binding,
3048                    actual: decl.ty,
3049                    expected: "UniformBuffer, StorageBuffer or ReadonlyStorageBuffer",
3050                })
3051            }
3052        };
3053
3054        let (pub_usage, internal_use, range_limit) = match binding_ty {
3055            wgt::BufferBindingType::Uniform => (
3056                wgt::BufferUsages::UNIFORM,
3057                wgt::BufferUses::UNIFORM,
3058                self.limits.max_uniform_buffer_binding_size,
3059            ),
3060            wgt::BufferBindingType::Storage { read_only } => (
3061                wgt::BufferUsages::STORAGE,
3062                if read_only {
3063                    wgt::BufferUses::STORAGE_READ_ONLY
3064                } else {
3065                    wgt::BufferUses::STORAGE_READ_WRITE
3066                },
3067                self.limits.max_storage_buffer_binding_size,
3068            ),
3069        };
3070
3071        let (align, align_limit_name) =
3072            binding_model::buffer_binding_type_alignment(&self.limits, binding_ty);
3073        if !bb.offset.is_multiple_of(align as u64) {
3074            return Err(Error::UnalignedBufferOffset(
3075                bb.offset,
3076                align_limit_name,
3077                align,
3078            ));
3079        }
3080
3081        let buffer = &bb.buffer;
3082
3083        used.buffers.insert_single(buffer.clone(), internal_use);
3084
3085        buffer.same_device(self)?;
3086
3087        buffer.check_usage(pub_usage)?;
3088
3089        let req_size = match bb.size.map(wgt::BufferSize::new) {
3090            // Requested a non-zero size
3091            Some(non_zero @ Some(_)) => non_zero,
3092            // Requested size not specified
3093            None => None,
3094            // Requested zero size
3095            Some(None) => return Err(CreateBindGroupError::BindingZeroSize(buffer.error_ident())),
3096        };
3097        let (bb, bind_size) = buffer.binding(bb.offset, req_size, snatch_guard)?;
3098
3099        if matches!(binding_ty, wgt::BufferBindingType::Storage { .. })
3100            && bind_size % u64::from(wgt::STORAGE_BINDING_SIZE_ALIGNMENT) != 0
3101        {
3102            return Err(Error::UnalignedEffectiveBufferBindingSizeForStorage {
3103                alignment: wgt::STORAGE_BINDING_SIZE_ALIGNMENT,
3104                size: bind_size,
3105            });
3106        }
3107
3108        let bind_end = bb.offset + bind_size;
3109
3110        if bind_size > range_limit {
3111            return Err(Error::BufferRangeTooLarge {
3112                binding,
3113                given: bind_size,
3114                limit: range_limit,
3115            });
3116        }
3117
3118        // Record binding info for validating dynamic offsets
3119        if dynamic {
3120            dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
3121                binding_idx: binding,
3122                buffer_size: buffer.size,
3123                binding_range: bb.offset..bind_end,
3124                maximum_dynamic_offset: buffer.size - bind_end,
3125                binding_type: binding_ty,
3126            });
3127        }
3128
3129        if let Some(non_zero) = min_size {
3130            let min_size = non_zero.get();
3131            if min_size > bind_size {
3132                return Err(Error::BindingSizeTooSmall {
3133                    buffer: buffer.error_ident(),
3134                    actual: bind_size,
3135                    min: min_size,
3136                });
3137            }
3138        } else {
3139            let late_size = wgt::BufferSize::new(bind_size)
3140                .ok_or_else(|| Error::BindingZeroSize(buffer.error_ident()))?;
3141            late_buffer_binding_sizes.insert(binding, late_size);
3142        }
3143
3144        // This was checked against the device's alignment requirements above,
3145        // which should always be a multiple of `COPY_BUFFER_ALIGNMENT`.
3146        assert_eq!(bb.offset % wgt::COPY_BUFFER_ALIGNMENT, 0);
3147
3148        let init_range = if dynamic {
3149            // We don't know what part of the buffer will be bound, so require that it
3150            // is fully initialized.
3151            0..buffer.size
3152        } else {
3153            // `wgpu_hal` only restricts shader access to bound buffer regions with
3154            // a certain resolution. For the sake of lazy initialization, round up
3155            // the size of the bound range to reflect how much of the buffer is
3156            // actually going to be visible to the shader.
3157            let bounds_check_alignment = binding_model::buffer_binding_type_bounds_check_alignment(
3158                &self.alignments,
3159                binding_ty,
3160            );
3161            let visible_size = align_to(bind_size, bounds_check_alignment);
3162
3163            bb.offset..bb.offset + visible_size
3164        };
3165
3166        buffer_init_actions.extend(buffer.initialization_status.read().create_action(
3167            buffer,
3168            init_range,
3169            MemoryInitKind::NeedsInitializedMemory,
3170        ));
3171
3172        Ok(bb)
3173    }
3174
3175    fn create_sampler_binding<'a>(
3176        &self,
3177        used: &mut BindGroupStates,
3178        binding: u32,
3179        decl: &wgt::BindGroupLayoutEntry,
3180        sampler: &'a Arc<Sampler>,
3181    ) -> Result<&'a dyn hal::DynSampler, CreateBindGroupError> {
3182        use crate::binding_model::CreateBindGroupError as Error;
3183
3184        used.samplers.insert_single(sampler.clone());
3185
3186        sampler.same_device(self)?;
3187
3188        match decl.ty {
3189            wgt::BindingType::Sampler(ty) => {
3190                let (allowed_filtering, allowed_comparison) = match ty {
3191                    wgt::SamplerBindingType::Filtering => (None, false),
3192                    wgt::SamplerBindingType::NonFiltering => (Some(false), false),
3193                    wgt::SamplerBindingType::Comparison => (None, true),
3194                };
3195                if let Some(allowed_filtering) = allowed_filtering {
3196                    if allowed_filtering != sampler.filtering {
3197                        return Err(Error::WrongSamplerFiltering {
3198                            binding,
3199                            layout_flt: allowed_filtering,
3200                            sampler_flt: sampler.filtering,
3201                        });
3202                    }
3203                }
3204                if allowed_comparison != sampler.comparison {
3205                    return Err(Error::WrongSamplerComparison {
3206                        binding,
3207                        layout_cmp: allowed_comparison,
3208                        sampler_cmp: sampler.comparison,
3209                    });
3210                }
3211            }
3212            _ => {
3213                return Err(Error::WrongBindingType {
3214                    binding,
3215                    actual: decl.ty,
3216                    expected: "Sampler",
3217                })
3218            }
3219        }
3220
3221        Ok(sampler.raw())
3222    }
3223
3224    fn create_texture_binding<'a>(
3225        &self,
3226        binding: u32,
3227        decl: &wgt::BindGroupLayoutEntry,
3228        view: &'a Arc<TextureView>,
3229        used: &mut BindGroupStates,
3230        texture_init_actions: &mut Vec<TextureInitTrackerAction>,
3231        snatch_guard: &'a SnatchGuard<'a>,
3232    ) -> Result<hal::TextureBinding<'a, dyn hal::DynTextureView>, CreateBindGroupError> {
3233        view.same_device(self)?;
3234
3235        let internal_use = self.texture_use_parameters(
3236            binding,
3237            decl,
3238            view,
3239            "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
3240        )?;
3241
3242        used.views.insert_single(view.clone(), internal_use);
3243
3244        let texture = &view.parent;
3245
3246        texture_init_actions.push(TextureInitTrackerAction {
3247            texture: texture.clone(),
3248            range: TextureInitRange {
3249                mip_range: view.desc.range.mip_range(texture.desc.mip_level_count),
3250                layer_range: view
3251                    .desc
3252                    .range
3253                    .layer_range(texture.desc.array_layer_count()),
3254            },
3255            kind: MemoryInitKind::NeedsInitializedMemory,
3256        });
3257
3258        Ok(hal::TextureBinding {
3259            view: view.try_raw(snatch_guard)?,
3260            usage: internal_use,
3261        })
3262    }
3263
3264    fn create_tlas_binding<'a>(
3265        self: &Arc<Self>,
3266        used: &mut BindGroupStates,
3267        binding: u32,
3268        decl: &wgt::BindGroupLayoutEntry,
3269        tlas: &'a Arc<Tlas>,
3270        snatch_guard: &'a SnatchGuard<'a>,
3271    ) -> Result<&'a dyn hal::DynAccelerationStructure, CreateBindGroupError> {
3272        use crate::binding_model::CreateBindGroupError as Error;
3273
3274        used.acceleration_structures.insert_single(tlas.clone());
3275
3276        tlas.same_device(self)?;
3277
3278        match decl.ty {
3279            wgt::BindingType::AccelerationStructure { vertex_return } => {
3280                if vertex_return
3281                    && !tlas.flags.contains(
3282                        wgpu_types::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,
3283                    )
3284                {
3285                    return Err(Error::MissingTLASVertexReturn { binding });
3286                }
3287            }
3288            _ => {
3289                return Err(Error::WrongBindingType {
3290                    binding,
3291                    actual: decl.ty,
3292                    expected: "Tlas",
3293                });
3294            }
3295        }
3296
3297        Ok(tlas.try_raw(snatch_guard)?)
3298    }
3299
3300    fn create_external_texture_binding<'a>(
3301        &'a self,
3302        binding: u32,
3303        decl: &wgt::BindGroupLayoutEntry,
3304        external_texture: &'a Arc<ExternalTexture>,
3305        used: &mut BindGroupStates,
3306        snatch_guard: &'a SnatchGuard,
3307    ) -> Result<
3308        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3309        CreateBindGroupError,
3310    > {
3311        use crate::binding_model::CreateBindGroupError as Error;
3312
3313        external_texture.same_device(self)?;
3314
3315        used.external_textures
3316            .insert_single(external_texture.clone());
3317
3318        match decl.ty {
3319            wgt::BindingType::ExternalTexture => {}
3320            _ => {
3321                return Err(Error::WrongBindingType {
3322                    binding,
3323                    actual: decl.ty,
3324                    expected: "ExternalTexture",
3325                });
3326            }
3327        }
3328
3329        let planes = (0..3)
3330            .map(|i| {
3331                // We always need 3 bindings. If we have fewer than 3 planes
3332                // just bind plane 0 multiple times. The shader will only
3333                // sample from valid planes anyway.
3334                let plane = external_texture
3335                    .planes
3336                    .get(i)
3337                    .unwrap_or(&external_texture.planes[0]);
3338                let internal_use = wgt::TextureUses::RESOURCE;
3339                used.views.insert_single(plane.clone(), internal_use);
3340                let view = plane.try_raw(snatch_guard)?;
3341                Ok(hal::TextureBinding {
3342                    view,
3343                    usage: internal_use,
3344                })
3345            })
3346            // We can remove this intermediate Vec by using
3347            // array::try_from_fn() above, once it stabilizes.
3348            .collect::<Result<Vec<_>, Error>>()?;
3349        let planes = planes.try_into().unwrap();
3350
3351        used.buffers
3352            .insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);
3353        let params = external_texture.params.binding(0, None, snatch_guard)?.0;
3354
3355        Ok(hal::ExternalTextureBinding { planes, params })
3356    }
3357
3358    fn create_external_texture_binding_from_view<'a>(
3359        &'a self,
3360        binding: u32,
3361        decl: &wgt::BindGroupLayoutEntry,
3362        view: &'a Arc<TextureView>,
3363        used: &mut BindGroupStates,
3364        snatch_guard: &'a SnatchGuard,
3365    ) -> Result<
3366        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3367        CreateBindGroupError,
3368    > {
3369        use crate::binding_model::CreateBindGroupError as Error;
3370
3371        view.same_device(self)?;
3372
3373        let internal_use = self.texture_use_parameters(binding, decl, view, "SampledTexture")?;
3374        used.views.insert_single(view.clone(), internal_use);
3375
3376        match decl.ty {
3377            wgt::BindingType::ExternalTexture => {}
3378            _ => {
3379                return Err(Error::WrongBindingType {
3380                    binding,
3381                    actual: decl.ty,
3382                    expected: "ExternalTexture",
3383                });
3384            }
3385        }
3386
3387        // We need 3 bindings, so just repeat the same texture view 3 times.
3388        let planes = [
3389            hal::TextureBinding {
3390                view: view.try_raw(snatch_guard)?,
3391                usage: internal_use,
3392            },
3393            hal::TextureBinding {
3394                view: view.try_raw(snatch_guard)?,
3395                usage: internal_use,
3396            },
3397            hal::TextureBinding {
3398                view: view.try_raw(snatch_guard)?,
3399                usage: internal_use,
3400            },
3401        ];
3402        let params = hal::BufferBinding::new_unchecked(
3403            self.default_external_texture_params_buffer.as_ref(),
3404            0,
3405            None,
3406        );
3407
3408        Ok(hal::ExternalTextureBinding { planes, params })
3409    }
3410
3411    // This function expects the provided bind group layout to be resolved
3412    // (not passing a duplicate) beforehand.
3413    pub fn create_bind_group(
3414        self: &Arc<Self>,
3415        desc: binding_model::ResolvedBindGroupDescriptor,
3416    ) -> Result<Arc<BindGroup>, CreateBindGroupError> {
3417        use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};
3418
3419        let layout = desc.layout;
3420
3421        self.check_is_valid()?;
3422        layout.same_device(self)?;
3423        layout.check_is_valid()?;
3424
3425        {
3426            // Check that the number of entries in the descriptor matches
3427            // the number of entries in the layout.
3428            let actual = desc.entries.len();
3429            let expected = layout.entries.len();
3430            if actual != expected {
3431                return Err(Error::BindingsNumMismatch { expected, actual });
3432            }
3433        }
3434
3435        // TODO: arrayvec/smallvec, or re-use allocations
3436        // Record binding info for dynamic offset validation
3437        let mut dynamic_binding_info = Vec::new();
3438        // Map of binding -> shader reflected size
3439        //Note: we can't collect into a vector right away because
3440        // it needs to be in BGL iteration order, not BG entry order.
3441        let mut late_buffer_binding_sizes = FastHashMap::default();
3442        // fill out the descriptors
3443        let mut used = BindGroupStates::new();
3444
3445        let mut buffer_init_actions = Vec::new();
3446        let mut texture_init_actions = Vec::new();
3447        let mut hal_entries = Vec::with_capacity(desc.entries.len());
3448        let mut hal_buffers = Vec::new();
3449        let mut hal_samplers = Vec::new();
3450        let mut hal_textures = Vec::new();
3451        let mut hal_tlas_s = Vec::new();
3452        let mut hal_external_textures = Vec::new();
3453        let snatch_guard = self.snatchable_lock.read();
3454        for entry in desc.entries.iter() {
3455            let binding = entry.binding;
3456            // Find the corresponding declaration in the layout
3457            let decl = layout
3458                .entries
3459                .get(binding)
3460                .ok_or(Error::MissingBindingDeclaration(binding))?;
3461            let (res_index, count) = match entry.resource {
3462                Br::Buffer(ref bb) => {
3463                    let bb = self.create_buffer_binding(
3464                        bb,
3465                        binding,
3466                        decl,
3467                        &mut buffer_init_actions,
3468                        &mut dynamic_binding_info,
3469                        &mut late_buffer_binding_sizes,
3470                        &mut used,
3471                        &snatch_guard,
3472                    )?;
3473
3474                    let res_index = hal_buffers.len();
3475                    hal_buffers.push(bb);
3476                    (res_index, 1)
3477                }
3478                Br::BufferArray(ref bindings_array) => {
3479                    let num_bindings = bindings_array.len();
3480                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3481
3482                    let res_index = hal_buffers.len();
3483                    for bb in bindings_array.iter() {
3484                        let bb = self.create_buffer_binding(
3485                            bb,
3486                            binding,
3487                            decl,
3488                            &mut buffer_init_actions,
3489                            &mut dynamic_binding_info,
3490                            &mut late_buffer_binding_sizes,
3491                            &mut used,
3492                            &snatch_guard,
3493                        )?;
3494                        hal_buffers.push(bb);
3495                    }
3496                    (res_index, num_bindings)
3497                }
3498                Br::Sampler(ref sampler) => {
3499                    let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3500
3501                    let res_index = hal_samplers.len();
3502                    hal_samplers.push(sampler);
3503                    (res_index, 1)
3504                }
3505                Br::SamplerArray(ref samplers) => {
3506                    let num_bindings = samplers.len();
3507                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3508
3509                    let res_index = hal_samplers.len();
3510                    for sampler in samplers.iter() {
3511                        let sampler =
3512                            self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3513
3514                        hal_samplers.push(sampler);
3515                    }
3516
3517                    (res_index, num_bindings)
3518                }
3519                Br::TextureView(ref view) => match decl.ty {
3520                    wgt::BindingType::ExternalTexture => {
3521                        let et = self.create_external_texture_binding_from_view(
3522                            binding,
3523                            decl,
3524                            view,
3525                            &mut used,
3526                            &snatch_guard,
3527                        )?;
3528                        let res_index = hal_external_textures.len();
3529                        hal_external_textures.push(et);
3530                        (res_index, 1)
3531                    }
3532                    _ => {
3533                        let tb = self.create_texture_binding(
3534                            binding,
3535                            decl,
3536                            view,
3537                            &mut used,
3538                            &mut texture_init_actions,
3539                            &snatch_guard,
3540                        )?;
3541                        let res_index = hal_textures.len();
3542                        hal_textures.push(tb);
3543                        (res_index, 1)
3544                    }
3545                },
3546                Br::TextureViewArray(ref views) => {
3547                    let num_bindings = views.len();
3548                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3549
3550                    let res_index = hal_textures.len();
3551                    for view in views.iter() {
3552                        let tb = self.create_texture_binding(
3553                            binding,
3554                            decl,
3555                            view,
3556                            &mut used,
3557                            &mut texture_init_actions,
3558                            &snatch_guard,
3559                        )?;
3560
3561                        hal_textures.push(tb);
3562                    }
3563
3564                    (res_index, num_bindings)
3565                }
3566                Br::AccelerationStructure(ref tlas) => {
3567                    let tlas =
3568                        self.create_tlas_binding(&mut used, binding, decl, tlas, &snatch_guard)?;
3569                    let res_index = hal_tlas_s.len();
3570                    hal_tlas_s.push(tlas);
3571                    (res_index, 1)
3572                }
3573                Br::AccelerationStructureArray(ref tlas_array) => {
3574                    // Feature validation for TLAS binding arrays happens at bind group layout
3575                    // creation time (mirroring other binding-array resource types). By the time we
3576                    // get here, `decl.count` has already been validated against device features.
3577                    let num_bindings = tlas_array.len();
3578                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3579
3580                    let res_index = hal_tlas_s.len();
3581                    for tlas in tlas_array.iter() {
3582                        let tlas = self.create_tlas_binding(
3583                            &mut used,
3584                            binding,
3585                            decl,
3586                            tlas,
3587                            &snatch_guard,
3588                        )?;
3589                        hal_tlas_s.push(tlas);
3590                    }
3591                    (res_index, num_bindings)
3592                }
3593                Br::ExternalTexture(ref et) => {
3594                    let et = self.create_external_texture_binding(
3595                        binding,
3596                        decl,
3597                        et,
3598                        &mut used,
3599                        &snatch_guard,
3600                    )?;
3601                    let res_index = hal_external_textures.len();
3602                    hal_external_textures.push(et);
3603                    (res_index, 1)
3604                }
3605            };
3606
3607            hal_entries.push(hal::BindGroupEntry {
3608                binding,
3609                resource_index: res_index as u32,
3610                count: count as u32,
3611            });
3612        }
3613
3614        used.optimize();
3615
3616        hal_entries.sort_by_key(|entry| entry.binding);
3617        for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
3618            if a.binding == b.binding {
3619                return Err(Error::DuplicateBinding(a.binding));
3620            }
3621        }
3622
3623        dynamic_binding_info.sort_by_key(|i| i.binding_idx);
3624
3625        let hal_desc = hal::BindGroupDescriptor {
3626            label: desc.label.to_hal(self.instance_flags),
3627            layout: layout.try_raw()?,
3628            entries: &hal_entries,
3629            buffers: &hal_buffers,
3630            samplers: &hal_samplers,
3631            textures: &hal_textures,
3632            acceleration_structures: &hal_tlas_s,
3633            external_textures: &hal_external_textures,
3634        };
3635        let raw = unsafe { self.raw().create_bind_group(&hal_desc) }
3636            .map_err(|e| self.handle_hal_error(e))?;
3637
3638        // collect in the order of BGL iteration
3639        let late_buffer_binding_infos = layout
3640            .entries
3641            .indices()
3642            .flat_map(|binding| {
3643                let size = late_buffer_binding_sizes.get(&binding).cloned()?;
3644                Some(BindGroupLateBufferBindingInfo {
3645                    binding_index: binding,
3646                    size,
3647                })
3648            })
3649            .collect();
3650
3651        let bind_group = BindGroup {
3652            raw: Snatchable::new(raw),
3653            device: self.clone(),
3654            layout,
3655            label: desc.label.to_string(),
3656            tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),
3657            used,
3658            buffer_init_actions,
3659            texture_init_actions,
3660            dynamic_binding_info,
3661            late_buffer_binding_infos,
3662        };
3663
3664        let bind_group = Arc::new(bind_group);
3665
3666        let weak_ref = Arc::downgrade(&bind_group);
3667        for texture in bind_group.used.views.used_textures() {
3668            let mut bind_groups = texture.bind_groups.lock();
3669            bind_groups.push(weak_ref.clone());
3670        }
3671        for buffer in bind_group.used.buffers.used_resources() {
3672            let mut bind_groups = buffer.bind_groups.lock();
3673            bind_groups.push(weak_ref.clone());
3674        }
3675
3676        Ok(bind_group)
3677    }
3678
3679    fn check_array_binding(
3680        features: wgt::Features,
3681        count: Option<NonZeroU32>,
3682        num_bindings: usize,
3683    ) -> Result<(), CreateBindGroupError> {
3684        use super::binding_model::CreateBindGroupError as Error;
3685
3686        if let Some(count) = count {
3687            let count = count.get() as usize;
3688            if count < num_bindings {
3689                return Err(Error::BindingArrayPartialLengthMismatch {
3690                    actual: num_bindings,
3691                    expected: count,
3692                });
3693            }
3694            if count != num_bindings
3695                && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
3696            {
3697                return Err(Error::BindingArrayLengthMismatch {
3698                    actual: num_bindings,
3699                    expected: count,
3700                });
3701            }
3702            if num_bindings == 0 {
3703                return Err(Error::BindingArrayZeroLength);
3704            }
3705        } else {
3706            return Err(Error::SingleBindingExpected);
3707        };
3708
3709        Ok(())
3710    }
3711
3712    fn texture_use_parameters(
3713        &self,
3714        binding: u32,
3715        decl: &wgt::BindGroupLayoutEntry,
3716        view: &TextureView,
3717        expected: &'static str,
3718    ) -> Result<wgt::TextureUses, CreateBindGroupError> {
3719        use crate::binding_model::CreateBindGroupError as Error;
3720        if view
3721            .desc
3722            .aspects()
3723            .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
3724        {
3725            return Err(Error::DepthStencilAspect);
3726        }
3727        match decl.ty {
3728            wgt::BindingType::Texture {
3729                sample_type,
3730                view_dimension,
3731                multisampled,
3732            } => {
3733                use wgt::TextureSampleType as Tst;
3734                if multisampled != (view.samples != 1) {
3735                    return Err(Error::InvalidTextureMultisample {
3736                        binding,
3737                        layout_multisampled: multisampled,
3738                        view_samples: view.samples,
3739                    });
3740                }
3741                let compat_sample_type = view
3742                    .desc
3743                    .format
3744                    .sample_type(Some(view.desc.range.aspect), Some(self.features))
3745                    .unwrap();
3746                match (sample_type, compat_sample_type) {
3747                    (Tst::Uint, Tst::Uint) |
3748                        (Tst::Sint, Tst::Sint) |
3749                        (Tst::Depth, Tst::Depth) |
3750                        // if we expect non-filterable, accept anything float
3751                        (Tst::Float { filterable: false }, Tst::Float { .. }) |
3752                        // if we expect filterable, require it
3753                        (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
3754                        // if we expect non-filterable, also accept depth
3755                        (Tst::Float { filterable: false }, Tst::Depth) => {}
3756                    // if we expect filterable, also accept Float that is defined as
3757                    // unfilterable if filterable feature is explicitly enabled (only hit
3758                    // if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is
3759                    // enabled)
3760                    (Tst::Float { filterable: true }, Tst::Float { .. })
3761                        if view.format_features.flags
3762                            .contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
3763                    _ => {
3764                        return Err(Error::InvalidTextureSampleType {
3765                            binding,
3766                            layout_sample_type: sample_type,
3767                            view_format: view.desc.format,
3768                            view_sample_type: compat_sample_type,
3769                        })
3770                    }
3771                }
3772                if view_dimension != view.desc.dimension {
3773                    return Err(Error::InvalidTextureDimension {
3774                        binding,
3775                        layout_dimension: view_dimension,
3776                        view_dimension: view.desc.dimension,
3777                    });
3778                }
3779                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3780                Ok(wgt::TextureUses::RESOURCE)
3781            }
3782            wgt::BindingType::StorageTexture {
3783                access,
3784                format,
3785                view_dimension,
3786            } => {
3787                if format != view.desc.format {
3788                    return Err(Error::InvalidStorageTextureFormat {
3789                        binding,
3790                        layout_format: format,
3791                        view_format: view.desc.format,
3792                    });
3793                }
3794                if view_dimension != view.desc.dimension {
3795                    return Err(Error::InvalidTextureDimension {
3796                        binding,
3797                        layout_dimension: view_dimension,
3798                        view_dimension: view.desc.dimension,
3799                    });
3800                }
3801
3802                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3803                if mip_level_count != 1 {
3804                    return Err(Error::InvalidStorageTextureMipLevelCount {
3805                        binding,
3806                        mip_level_count,
3807                    });
3808                }
3809
3810                view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;
3811
3812                Ok(match access {
3813                    wgt::StorageTextureAccess::ReadOnly => wgt::TextureUses::STORAGE_READ_ONLY,
3814                    wgt::StorageTextureAccess::WriteOnly => wgt::TextureUses::STORAGE_WRITE_ONLY,
3815                    wgt::StorageTextureAccess::ReadWrite => wgt::TextureUses::STORAGE_READ_WRITE,
3816                    wgt::StorageTextureAccess::Atomic => wgt::TextureUses::STORAGE_ATOMIC,
3817                })
3818            }
3819            wgt::BindingType::ExternalTexture => {
3820                if view.desc.dimension != TextureViewDimension::D2 {
3821                    return Err(Error::InvalidTextureDimension {
3822                        binding,
3823                        layout_dimension: TextureViewDimension::D2,
3824                        view_dimension: view.desc.dimension,
3825                    });
3826                }
3827                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3828                if mip_level_count != 1 {
3829                    return Err(Error::InvalidExternalTextureMipLevelCount {
3830                        binding,
3831                        mip_level_count,
3832                    });
3833                }
3834                if view.desc.format != TextureFormat::Rgba8Unorm
3835                    && view.desc.format != TextureFormat::Bgra8Unorm
3836                    && view.desc.format != TextureFormat::Rgba16Float
3837                {
3838                    return Err(Error::InvalidExternalTextureFormat {
3839                        binding,
3840                        format: view.desc.format,
3841                    });
3842                }
3843                if view.samples != 1 {
3844                    return Err(Error::InvalidTextureMultisample {
3845                        binding,
3846                        layout_multisampled: false,
3847                        view_samples: view.samples,
3848                    });
3849                }
3850
3851                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3852                Ok(wgt::TextureUses::RESOURCE)
3853            }
3854            _ => Err(Error::WrongBindingType {
3855                binding,
3856                actual: decl.ty,
3857                expected,
3858            }),
3859        }
3860    }
3861
3862    pub fn create_pipeline_layout(
3863        self: &Arc<Self>,
3864        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3865    ) -> (
3866        Arc<binding_model::PipelineLayout>,
3867        Option<binding_model::CreatePipelineLayoutError>,
3868    ) {
3869        let (layout, error) = match self.create_pipeline_layout_impl(desc, false) {
3870            Ok(layout) => (layout, None),
3871            Err(e) => (
3872                binding_model::PipelineLayout::invalid(Arc::clone(self), desc.label.to_string()),
3873                Some(e),
3874            ),
3875        };
3876        #[cfg(feature = "trace")]
3877        if let Some(ref mut trace) = *self.trace.lock() {
3878            use crate::device::trace::IntoTrace;
3879            trace.add(trace::Action::CreatePipelineLayout(
3880                layout.to_trace(),
3881                desc.to_trace(),
3882            ));
3883        }
3884        api_log!(
3885            "Device::create_pipeline_layout -> {:?}",
3886            Arc::as_ptr(&layout)
3887        );
3888        (layout, error)
3889    }
3890
3891    fn create_pipeline_layout_impl(
3892        self: &Arc<Self>,
3893        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3894        ignore_exclusive_pipeline_check: bool,
3895    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
3896        use crate::binding_model::CreatePipelineLayoutError as Error;
3897
3898        self.check_is_valid()?;
3899
3900        let bind_group_layouts_count = desc.bind_group_layouts.len();
3901        let device_max_bind_groups = self.limits.max_bind_groups as usize;
3902        if bind_group_layouts_count > device_max_bind_groups {
3903            return Err(Error::TooManyGroups {
3904                actual: bind_group_layouts_count,
3905                max: device_max_bind_groups,
3906            });
3907        }
3908
3909        if desc.immediate_size != 0 {
3910            self.require_features(wgt::Features::IMMEDIATES)?;
3911        }
3912        if self.limits.max_immediate_size < desc.immediate_size {
3913            return Err(Error::ImmediateRangeTooLarge {
3914                size: desc.immediate_size,
3915                max: self.limits.max_immediate_size,
3916            });
3917        }
3918        if !desc
3919            .immediate_size
3920            .is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT)
3921        {
3922            return Err(Error::MisalignedImmediateSize {
3923                size: desc.immediate_size,
3924            });
3925        }
3926
3927        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
3928
3929        for (index, bgl) in desc.bind_group_layouts.iter().enumerate() {
3930            let Some(bgl) = bgl else {
3931                continue;
3932            };
3933
3934            bgl.same_device(self)?;
3935
3936            if !ignore_exclusive_pipeline_check {
3937                let exclusive_pipeline = bgl.exclusive_pipeline.get().unwrap();
3938                if !matches!(exclusive_pipeline, binding_model::ExclusivePipeline::None) {
3939                    return Err(Error::BglHasExclusivePipeline {
3940                        index,
3941                        pipeline: alloc::format!("{exclusive_pipeline}"),
3942                    });
3943                }
3944            }
3945
3946            count_validator.merge(&bgl.state()?.binding_count_validator);
3947        }
3948
3949        count_validator
3950            .validate(&self.limits, self.instance_flags)
3951            .map_err(Error::TooManyBindings)?;
3952
3953        let buffers_and_acceleration_structures_in_vertex_stage =
3954            count_validator.buffers_and_acceleration_structures_in_vertex_stage();
3955
3956        let get_bgl_iter = || {
3957            desc.bind_group_layouts
3958                .iter()
3959                .map(|bgl| bgl.as_ref().filter(|bgl| !bgl.entries.is_empty()))
3960        };
3961
3962        let bind_group_layouts = get_bgl_iter()
3963            .map(|bgl| bgl.cloned())
3964            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3965
3966        let raw_bind_group_layouts = get_bgl_iter()
3967            .map(|bgl| bgl.map(|bgl| bgl.try_raw()).transpose())
3968            .collect::<Result<ArrayVec<_, { hal::MAX_BIND_GROUPS }>, _>>()?;
3969
3970        let additional_flags = if self.indirect_validation.is_some() {
3971            hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE
3972        } else {
3973            hal::PipelineLayoutFlags::empty()
3974        };
3975
3976        let hal_desc = hal::PipelineLayoutDescriptor {
3977            label: desc.label.to_hal(self.instance_flags),
3978            flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
3979                | hal::PipelineLayoutFlags::NUM_WORK_GROUPS
3980                | additional_flags,
3981            bind_group_layouts: &raw_bind_group_layouts,
3982            immediate_size: desc.immediate_size,
3983        };
3984
3985        let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }
3986            .map_err(|e| self.handle_hal_error(e))?;
3987
3988        drop(raw_bind_group_layouts);
3989
3990        let layout = binding_model::PipelineLayout {
3991            raw: ResourceState::Valid(raw),
3992            device: self.clone(),
3993            label: desc.label.to_string(),
3994            bind_group_layouts,
3995            immediate_size: desc.immediate_size,
3996            buffers_and_acceleration_structures_in_vertex_stage,
3997        };
3998
3999        let layout = Arc::new(layout);
4000
4001        Ok(layout)
4002    }
4003
4004    fn create_derived_pipeline_layout(
4005        self: &Arc<Self>,
4006        mut derived_group_layouts: Box<ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>>,
4007        immediate_size: u32,
4008    ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {
4009        while derived_group_layouts
4010            .last()
4011            .is_some_and(|map| map.is_empty())
4012        {
4013            derived_group_layouts.pop();
4014        }
4015
4016        let mut unique_bind_group_layouts = FastHashMap::default();
4017
4018        let bind_group_layouts = derived_group_layouts
4019            .into_iter()
4020            .map(|mut bgl_entry_map| {
4021                if bgl_entry_map.is_empty() {
4022                    return Ok(None);
4023                }
4024
4025                bgl_entry_map.sort();
4026                match unique_bind_group_layouts.entry(bgl_entry_map) {
4027                    hashbrown::hash_map::Entry::Occupied(v) => Ok(Some(Arc::clone(v.get()))),
4028                    hashbrown::hash_map::Entry::Vacant(e) => {
4029                        match self.create_bind_group_layout_impl(
4030                            &None,
4031                            e.key().clone(),
4032                            bgl::Origin::Derived,
4033                        ) {
4034                            Ok(bgl) => {
4035                                e.insert(bgl.clone());
4036                                Ok(Some(bgl))
4037                            }
4038                            Err(e) => Err(e),
4039                        }
4040                    }
4041                }
4042            })
4043            .collect::<Result<Vec<_>, _>>()?;
4044
4045        let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {
4046            label: None,
4047            bind_group_layouts: Cow::Owned(bind_group_layouts),
4048            immediate_size,
4049        };
4050
4051        let layout = self.create_pipeline_layout_impl(&layout_desc, true)?;
4052        Ok(layout)
4053    }
4054
4055    pub fn create_compute_pipeline(
4056        self: &Arc<Self>,
4057        desc: pipeline::ResolvedComputePipelineDescriptor,
4058    ) -> (
4059        Arc<pipeline::ComputePipeline>,
4060        Option<pipeline::CreateComputePipelineError>,
4061    ) {
4062        let (compute_pipeline, error) = match self.create_compute_pipeline_inner(desc.clone()) {
4063            Ok(compute_pipeline) => (compute_pipeline, None),
4064            Err(error) => (
4065                pipeline::ComputePipeline::invalid(self.clone(), desc.label.to_string()),
4066                Some(error),
4067            ),
4068        };
4069        #[cfg(feature = "trace")]
4070        if let Some(ref mut trace) = *self.trace.lock() {
4071            use crate::device::trace;
4072            use crate::device::trace::IntoTrace;
4073            trace.add(trace::Action::CreateComputePipeline {
4074                id: compute_pipeline.to_trace(),
4075                desc: desc.to_trace(),
4076            });
4077        }
4078        (compute_pipeline, error)
4079    }
4080
4081    pub fn create_compute_pipeline_inner(
4082        self: &Arc<Self>,
4083        desc: pipeline::ResolvedComputePipelineDescriptor,
4084    ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {
4085        self.check_is_valid()?;
4086
4087        self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
4088
4089        let shader_module = desc.stage.module;
4090
4091        shader_module.same_device(self)?;
4092
4093        let is_auto_layout = desc.layout.is_none();
4094
4095        // Get the pipeline layout from the desc if it is provided.
4096        let pipeline_layout = match desc.layout {
4097            Some(pipeline_layout) => {
4098                pipeline_layout.same_device(self)?;
4099                pipeline_layout.check_valid()?;
4100                Some(pipeline_layout)
4101            }
4102            None => None,
4103        };
4104
4105        let mut binding_layout_source = match pipeline_layout {
4106            Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4107            None => validation::BindingLayoutSource::new_derived(&self.limits),
4108        };
4109        let mut shader_binding_sizes = FastHashMap::default();
4110        let io = validation::StageIo::default();
4111
4112        let final_entry_point_name;
4113
4114        {
4115            let stage = validation::ShaderStageForValidation::Compute;
4116
4117            final_entry_point_name = shader_module.finalize_entry_point_name(
4118                stage.to_naga(),
4119                desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
4120            )?;
4121
4122            if let Some(interface) = shader_module.interface.interface() {
4123                let _ = interface.check_stage(
4124                    &mut binding_layout_source,
4125                    &mut shader_binding_sizes,
4126                    &final_entry_point_name,
4127                    stage,
4128                    io,
4129                    None,
4130                )?;
4131            }
4132        }
4133
4134        let pipeline_layout = match binding_layout_source {
4135            validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
4136            validation::BindingLayoutSource::Derived(entries) => {
4137                let immediate_size = shader_module
4138                    .interface
4139                    .interface()
4140                    .map_or(0, |i| i.immediate_size);
4141                self.create_derived_pipeline_layout(entries, immediate_size)?
4142            }
4143        };
4144
4145        let late_sized_buffer_groups =
4146            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
4147
4148        let cache = match desc.cache {
4149            Some(cache) => {
4150                cache.same_device(self)?;
4151                Some(cache)
4152            }
4153            None => None,
4154        };
4155
4156        let pipeline_desc = hal::ComputePipelineDescriptor {
4157            label: desc.label.to_hal(self.instance_flags),
4158            layout: pipeline_layout.raw()?,
4159            stage: hal::ProgrammableStage {
4160                module: shader_module.raw(),
4161                entry_point: final_entry_point_name.as_ref(),
4162                constants: &desc.stage.constants,
4163                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
4164            },
4165            cache: cache.as_ref().map(|it| it.raw()),
4166        };
4167
4168        let raw =
4169            unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(
4170                |err| match err {
4171                    hal::PipelineError::Device(error) => {
4172                        pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))
4173                    }
4174                    hal::PipelineError::Linkage(_stages, msg) => {
4175                        pipeline::CreateComputePipelineError::Internal(msg)
4176                    }
4177                    hal::PipelineError::EntryPoint(_stage) => {
4178                        pipeline::CreateComputePipelineError::Internal(
4179                            ENTRYPOINT_FAILURE_ERROR.to_string(),
4180                        )
4181                    }
4182                    hal::PipelineError::PipelineConstants(_stages, msg) => {
4183                        pipeline::CreateComputePipelineError::PipelineConstants(msg)
4184                    }
4185                },
4186            )?;
4187
4188        let immediate_slots_required =
4189            shader_module
4190                .interface
4191                .interface()
4192                .map_or(Default::default(), |iface| {
4193                    iface.immediate_slots_required(
4194                        naga::ShaderStage::Compute,
4195                        &final_entry_point_name,
4196                    )
4197                });
4198
4199        let pipeline = pipeline::ComputePipeline {
4200            state: ResourceState::Valid(pipeline::ComputePipelineState {
4201                raw: ManuallyDrop::new(raw),
4202                layout: pipeline_layout.clone(),
4203                _shader_module: shader_module,
4204            }),
4205            device: self.clone(),
4206            late_sized_buffer_groups,
4207            immediate_slots_required,
4208            label: desc.label.to_string(),
4209            tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),
4210        };
4211
4212        let pipeline = Arc::new(pipeline);
4213
4214        if is_auto_layout {
4215            for bgl in pipeline_layout.bind_group_layouts.iter() {
4216                let Some(bgl) = bgl else {
4217                    continue;
4218                };
4219
4220                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the
4221                // result.
4222                let _ = bgl.exclusive_pipeline.set((&pipeline).into());
4223            }
4224        }
4225
4226        Ok(pipeline)
4227    }
4228
4229    pub fn create_render_pipeline(
4230        self: &Arc<Self>,
4231        desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
4232    ) -> (
4233        Arc<pipeline::RenderPipeline>,
4234        Option<pipeline::CreateRenderPipelineError>,
4235    ) {
4236        let (render_pipeline, error) = match self.create_render_pipeline_inner(desc.clone()) {
4237            Ok(pipeline) => (pipeline, None),
4238            Err(e) => (
4239                pipeline::RenderPipeline::invalid(self.clone(), desc.label.to_string()),
4240                Some(e),
4241            ),
4242        };
4243        #[cfg(feature = "trace")]
4244        if let Some(ref mut trace) = *self.trace.lock() {
4245            use crate::device::trace::IntoTrace;
4246            trace.add(trace::Action::CreateGeneralRenderPipeline {
4247                id: render_pipeline.to_trace(),
4248                desc: desc.to_trace(),
4249            });
4250        }
4251        (render_pipeline, error)
4252    }
4253
4254    pub fn create_render_pipeline_inner(
4255        self: &Arc<Self>,
4256        desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
4257    ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {
4258        use wgt::TextureFormatFeatureFlags as Tfff;
4259
4260        self.check_is_valid()?;
4261
4262        let mut shader_binding_sizes = FastHashMap::default();
4263
4264        let color_targets = desc
4265            .fragment
4266            .as_ref()
4267            .map_or(&[][..], |fragment| &fragment.targets);
4268        let depth_stencil_state = desc.depth_stencil.as_ref();
4269
4270        check_color_attachment_count(color_targets.len(), self.limits.max_color_attachments)?;
4271
4272        {
4273            let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
4274                color_targets.iter().filter_map(|x| x.as_ref()).collect();
4275            if !cts.is_empty() && {
4276                let first = &cts[0];
4277                cts[1..]
4278                    .iter()
4279                    .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
4280            } {
4281                self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
4282            }
4283        }
4284
4285        let mut io = validation::StageIo::default();
4286        let mut validated_stages = wgt::ShaderStages::empty();
4287
4288        let mut vertex_steps;
4289        let mut hal_vertex_buffer_layouts;
4290        let mut total_attributes;
4291        let mut dual_source_blending = false;
4292        let mut has_depth_attachment = false;
4293        if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
4294            if vertex.buffers.len() > self.limits.max_vertex_buffers as usize {
4295                return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
4296                    given: vertex.buffers.len() as u32,
4297                    limit: self.limits.max_vertex_buffers,
4298                });
4299            }
4300
4301            vertex_steps = Vec::with_capacity(vertex.buffers.len());
4302            hal_vertex_buffer_layouts = Vec::with_capacity(vertex.buffers.len());
4303            total_attributes = 0;
4304            for (i, vb_state) in vertex.buffers.iter().enumerate() {
4305                let Some(vb_state) = vb_state else {
4306                    vertex_steps.push(None);
4307                    hal_vertex_buffer_layouts.push(None);
4308                    continue;
4309                };
4310
4311                // https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpuvertexbufferlayout
4312
4313                if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
4314                    return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
4315                        index: i as u32,
4316                        given: vb_state.array_stride as u32,
4317                        limit: self.limits.max_vertex_buffer_array_stride,
4318                    });
4319                }
4320                if vb_state.array_stride % wgt::VERTEX_ALIGNMENT != 0 {
4321                    return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
4322                        index: i as u32,
4323                        stride: vb_state.array_stride,
4324                    });
4325                }
4326
4327                let max_stride = if vb_state.array_stride == 0 {
4328                    self.limits.max_vertex_buffer_array_stride as u64
4329                } else {
4330                    vb_state.array_stride
4331                };
4332                let mut last_stride = 0;
4333                for attribute in vb_state.attributes.iter() {
4334                    let attribute_stride = attribute.offset + attribute.format.size();
4335                    if attribute_stride > max_stride {
4336                        return Err(
4337                            pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge {
4338                                location: attribute.shader_location,
4339                                given: attribute_stride as u32,
4340                                limit: max_stride as u32,
4341                            },
4342                        );
4343                    }
4344
4345                    let required_offset_alignment = attribute.format.size().min(4);
4346                    if attribute.offset % required_offset_alignment != 0 {
4347                        return Err(
4348                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4349                                location: attribute.shader_location,
4350                                offset: attribute.offset,
4351                            },
4352                        );
4353                    }
4354
4355                    if attribute.shader_location >= self.limits.max_vertex_attributes {
4356                        return Err(
4357                            pipeline::CreateRenderPipelineError::VertexAttributeLocationTooLarge {
4358                                given: attribute.shader_location,
4359                                limit: self.limits.max_vertex_attributes,
4360                            },
4361                        );
4362                    }
4363
4364                    last_stride = last_stride.max(attribute_stride);
4365                }
4366
4367                vertex_steps.push(Some(pipeline::VertexStep {
4368                    stride: vb_state.array_stride,
4369                    last_stride,
4370                    mode: vb_state.step_mode,
4371                }));
4372                hal_vertex_buffer_layouts.push(if vb_state.attributes.is_empty() {
4373                    None
4374                } else {
4375                    Some(hal::VertexBufferLayout {
4376                        array_stride: vb_state.array_stride,
4377                        step_mode: vb_state.step_mode,
4378                        attributes: vb_state.attributes.as_ref(),
4379                    })
4380                });
4381
4382                for attribute in vb_state.attributes.iter() {
4383                    if attribute.offset >= 0x10000000 {
4384                        return Err(
4385                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4386                                location: attribute.shader_location,
4387                                offset: attribute.offset,
4388                            },
4389                        );
4390                    }
4391
4392                    if let wgt::VertexFormat::Float64
4393                    | wgt::VertexFormat::Float64x2
4394                    | wgt::VertexFormat::Float64x3
4395                    | wgt::VertexFormat::Float64x4 = attribute.format
4396                    {
4397                        self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
4398                    }
4399
4400                    let previous = io.varyings.insert(
4401                        attribute.shader_location,
4402                        validation::InterfaceVar::vertex_attribute(attribute.format),
4403                    );
4404
4405                    if previous.is_some() {
4406                        return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
4407                            attribute.shader_location,
4408                        ));
4409                    }
4410                }
4411                total_attributes += vb_state.attributes.len();
4412            }
4413
4414            if total_attributes > self.limits.max_vertex_attributes as usize {
4415                return Err(
4416                    pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
4417                        given: total_attributes as u32,
4418                        limit: self.limits.max_vertex_attributes,
4419                    },
4420                );
4421            }
4422        } else {
4423            vertex_steps = Vec::new();
4424            hal_vertex_buffer_layouts = Vec::new();
4425        };
4426
4427        if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
4428            return Err(
4429                pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
4430                    strip_index_format: desc.primitive.strip_index_format,
4431                    topology: desc.primitive.topology,
4432                },
4433            );
4434        }
4435
4436        if desc.primitive.unclipped_depth {
4437            self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
4438        }
4439
4440        if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
4441            self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
4442        }
4443        if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
4444            self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
4445        }
4446
4447        if desc.primitive.conservative {
4448            self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
4449        }
4450
4451        if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
4452            return Err(
4453                pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
4454            );
4455        }
4456
4457        let mut target_specified = false;
4458
4459        for (i, cs) in color_targets.iter().enumerate() {
4460            if let Some(cs) = cs.as_ref() {
4461                target_specified = true;
4462                let error = 'error: {
4463                    // This is expected to be the operative check for illegal write mask
4464                    // values (larger than 15), because WebGPU requires that it be validated
4465                    // on the device timeline.
4466                    if cs.write_mask.contains_unknown_bits() {
4467                        break 'error Some(ColorStateError::InvalidWriteMask(cs.write_mask));
4468                    }
4469
4470                    let format_features = self.describe_format_features(cs.format)?;
4471                    if !format_features
4472                        .allowed_usages
4473                        .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4474                    {
4475                        break 'error Some(ColorStateError::FormatNotRenderable(cs.format));
4476                    }
4477                    if cs.blend.is_some() && !format_features.flags.contains(Tfff::BLENDABLE) {
4478                        break 'error Some(ColorStateError::FormatNotBlendable(cs.format));
4479                    }
4480                    if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
4481                        break 'error Some(ColorStateError::FormatNotColor(cs.format));
4482                    }
4483
4484                    if desc.multisample.count > 1
4485                        && !format_features
4486                            .flags
4487                            .sample_count_supported(desc.multisample.count)
4488                    {
4489                        break 'error Some(ColorStateError::InvalidSampleCount(
4490                            desc.multisample.count,
4491                            cs.format,
4492                            cs.format
4493                                .guaranteed_format_features(self.features)
4494                                .flags
4495                                .supported_sample_counts(),
4496                            self.adapter
4497                                .get_texture_format_features(cs.format)
4498                                .flags
4499                                .supported_sample_counts(),
4500                        ));
4501                    }
4502
4503                    if let Some(blend_mode) = cs.blend {
4504                        for component in [&blend_mode.color, &blend_mode.alpha] {
4505                            for factor in [component.src_factor, component.dst_factor] {
4506                                if factor.ref_second_blend_source() {
4507                                    self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
4508                                    if i == 0 {
4509                                        dual_source_blending = true;
4510                                    } else {
4511                                        break 'error Some(
4512                                            ColorStateError::BlendFactorOnUnsupportedTarget {
4513                                                factor,
4514                                                target: i as u32,
4515                                            },
4516                                        );
4517                                    }
4518                                }
4519
4520                                if [wgt::BlendOperation::Min, wgt::BlendOperation::Max]
4521                                    .contains(&component.operation)
4522                                    && factor != wgt::BlendFactor::One
4523                                {
4524                                    break 'error Some(ColorStateError::InvalidMinMaxBlendFactor {
4525                                        factor,
4526                                        target: i as u32,
4527                                    });
4528                                }
4529                            }
4530                        }
4531                    }
4532
4533                    break 'error None;
4534                };
4535                if let Some(e) = error {
4536                    return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
4537                }
4538            }
4539        }
4540
4541        if dual_source_blending && color_targets.len() > 1 {
4542            return Err(
4543                pipeline::CreateRenderPipelineError::DualSourceBlendingWithMultipleColorTargets {
4544                    count: color_targets.len(),
4545                },
4546            );
4547        }
4548
4549        validation::validate_color_attachment_bytes_per_sample(
4550            color_targets.iter().flatten().map(|cs| cs.format),
4551            self.limits.max_color_attachment_bytes_per_sample,
4552        )
4553        .map_err(pipeline::CreateRenderPipelineError::ColorAttachment)?;
4554
4555        if let Some(ds) = depth_stencil_state {
4556            // See <https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpudepthstencilstate>.
4557            target_specified = true;
4558            let error = 'error: {
4559                if !ds.format.is_depth_stencil_format() {
4560                    // This error case is not redundant with the aspect check below when
4561                    // neither depth nor stencil is enabled at all.
4562                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepthOrStencil(
4563                        ds.format,
4564                    ));
4565                }
4566
4567                let format_features = self.describe_format_features(ds.format)?;
4568                if !format_features
4569                    .allowed_usages
4570                    .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4571                {
4572                    break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(
4573                        ds.format,
4574                    ));
4575                }
4576
4577                let aspect = hal::FormatAspects::from(ds.format);
4578                if aspect.contains(hal::FormatAspects::DEPTH) {
4579                    has_depth_attachment = true;
4580                } else if ds.is_depth_enabled() {
4581                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
4582                }
4583                if has_depth_attachment {
4584                    let Some(depth_write_enabled) = ds.depth_write_enabled else {
4585                        break 'error Some(
4586                            pipeline::DepthStencilStateError::MissingDepthWriteEnabled(ds.format),
4587                        );
4588                    };
4589
4590                    let depth_compare_required = depth_write_enabled
4591                        || ds.stencil.front.depth_fail_op != wgt::StencilOperation::Keep
4592                        || ds.stencil.back.depth_fail_op != wgt::StencilOperation::Keep;
4593                    if depth_compare_required && ds.depth_compare.is_none() {
4594                        break 'error Some(pipeline::DepthStencilStateError::MissingDepthCompare(
4595                            ds.format,
4596                        ));
4597                    }
4598                }
4599
4600                if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
4601                    break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(
4602                        ds.format,
4603                    ));
4604                }
4605                if desc.multisample.count > 1
4606                    && !format_features
4607                        .flags
4608                        .sample_count_supported(desc.multisample.count)
4609                {
4610                    break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(
4611                        desc.multisample.count,
4612                        ds.format,
4613                        ds.format
4614                            .guaranteed_format_features(self.features)
4615                            .flags
4616                            .supported_sample_counts(),
4617                        self.adapter
4618                            .get_texture_format_features(ds.format)
4619                            .flags
4620                            .supported_sample_counts(),
4621                    ));
4622                }
4623
4624                break 'error None;
4625            };
4626            if let Some(e) = error {
4627                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
4628            }
4629
4630            if ds.bias.clamp != 0.0 {
4631                self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
4632            }
4633
4634            if (ds.bias.is_enabled() || ds.bias.clamp != 0.0)
4635                && !desc.primitive.topology.is_triangles()
4636            {
4637                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(
4638                    pipeline::DepthStencilStateError::DepthBiasWithIncompatibleTopology(
4639                        desc.primitive.topology,
4640                    ),
4641                ));
4642            }
4643        }
4644
4645        if !target_specified {
4646            return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);
4647        }
4648
4649        let is_auto_layout = desc.layout.is_none();
4650
4651        // Get the pipeline layout from the desc if it is provided.
4652        let pipeline_layout = match desc.layout {
4653            Some(pipeline_layout) => {
4654                pipeline_layout.same_device(self)?;
4655                pipeline_layout.check_valid()?;
4656                Some(pipeline_layout)
4657            }
4658            None => None,
4659        };
4660
4661        let mut binding_layout_source = match pipeline_layout {
4662            Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4663            None => validation::BindingLayoutSource::new_derived(&self.limits),
4664        };
4665
4666        let samples = {
4667            let sc = desc.multisample.count;
4668            if sc == 0 || sc > 32 || !sc.is_power_of_two() {
4669                return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
4670            }
4671            sc
4672        };
4673
4674        let mut vertex_stage = None;
4675        let mut task_stage = None;
4676        let mut mesh_stage = None;
4677        let mut _vertex_entry_point_name = String::new();
4678        let mut _task_entry_point_name = String::new();
4679        let mut _mesh_entry_point_name = String::new();
4680        let mut immediate_slots_required = naga::valid::ImmediateSlots::default();
4681        match desc.vertex {
4682            pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) => {
4683                vertex_stage = {
4684                    let stage_desc = &vertex.stage;
4685                    let stage = validation::ShaderStageForValidation::Vertex {
4686                        topology: desc.primitive.topology,
4687                        compare_function: desc.depth_stencil.as_ref().and_then(|d| d.depth_compare),
4688                    };
4689                    let stage_bit = stage.to_wgt_bit();
4690
4691                    let vertex_shader_module = &stage_desc.module;
4692                    vertex_shader_module.same_device(self)?;
4693
4694                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4695                        stage: stage_bit,
4696                        error,
4697                    };
4698
4699                    _vertex_entry_point_name = vertex_shader_module
4700                        .finalize_entry_point_name(
4701                            stage.to_naga(),
4702                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4703                        )
4704                        .map_err(stage_err)?;
4705
4706                    if let Some(interface) = vertex_shader_module.interface.interface() {
4707                        immediate_slots_required |= interface
4708                            .immediate_slots_required(stage.to_naga(), &_vertex_entry_point_name);
4709                        io = interface
4710                            .check_stage(
4711                                &mut binding_layout_source,
4712                                &mut shader_binding_sizes,
4713                                &_vertex_entry_point_name,
4714                                stage,
4715                                io,
4716                                Some(desc.primitive.topology),
4717                            )
4718                            .map_err(stage_err)?;
4719                        validated_stages |= stage_bit;
4720                    }
4721                    Some(hal::ProgrammableStage {
4722                        module: vertex_shader_module.raw(),
4723                        entry_point: &_vertex_entry_point_name,
4724                        constants: &stage_desc.constants,
4725                        zero_initialize_workgroup_memory: stage_desc
4726                            .zero_initialize_workgroup_memory,
4727                    })
4728                };
4729            }
4730            pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {
4731                self.require_features(wgt::Features::EXPERIMENTAL_MESH_SHADER)?;
4732
4733                task_stage = if let Some(task) = task {
4734                    let stage_desc = &task.stage;
4735                    let stage = validation::ShaderStageForValidation::Task;
4736                    let stage_bit = stage.to_wgt_bit();
4737                    let task_shader_module = &stage_desc.module;
4738                    task_shader_module.same_device(self)?;
4739
4740                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4741                        stage: stage_bit,
4742                        error,
4743                    };
4744
4745                    _task_entry_point_name = task_shader_module
4746                        .finalize_entry_point_name(
4747                            stage.to_naga(),
4748                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4749                        )
4750                        .map_err(stage_err)?;
4751
4752                    if let Some(interface) = task_shader_module.interface.interface() {
4753                        immediate_slots_required |= interface
4754                            .immediate_slots_required(stage.to_naga(), &_task_entry_point_name);
4755                        io = interface
4756                            .check_stage(
4757                                &mut binding_layout_source,
4758                                &mut shader_binding_sizes,
4759                                &_task_entry_point_name,
4760                                stage,
4761                                io,
4762                                Some(desc.primitive.topology),
4763                            )
4764                            .map_err(stage_err)?;
4765                        validated_stages |= stage_bit;
4766                    }
4767                    Some(hal::ProgrammableStage {
4768                        module: task_shader_module.raw(),
4769                        entry_point: &_task_entry_point_name,
4770                        constants: &stage_desc.constants,
4771                        zero_initialize_workgroup_memory: stage_desc
4772                            .zero_initialize_workgroup_memory,
4773                    })
4774                } else {
4775                    None
4776                };
4777                mesh_stage = {
4778                    let stage_desc = &mesh.stage;
4779                    let stage = validation::ShaderStageForValidation::Mesh;
4780                    let stage_bit = stage.to_wgt_bit();
4781                    let mesh_shader_module = &stage_desc.module;
4782                    mesh_shader_module.same_device(self)?;
4783
4784                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4785                        stage: stage_bit,
4786                        error,
4787                    };
4788
4789                    _mesh_entry_point_name = mesh_shader_module
4790                        .finalize_entry_point_name(
4791                            stage.to_naga(),
4792                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4793                        )
4794                        .map_err(stage_err)?;
4795
4796                    if let Some(interface) = mesh_shader_module.interface.interface() {
4797                        immediate_slots_required |= interface
4798                            .immediate_slots_required(stage.to_naga(), &_mesh_entry_point_name);
4799                        io = interface
4800                            .check_stage(
4801                                &mut binding_layout_source,
4802                                &mut shader_binding_sizes,
4803                                &_mesh_entry_point_name,
4804                                stage,
4805                                io,
4806                                Some(desc.primitive.topology),
4807                            )
4808                            .map_err(stage_err)?;
4809                        validated_stages |= stage_bit;
4810                    }
4811                    Some(hal::ProgrammableStage {
4812                        module: mesh_shader_module.raw(),
4813                        entry_point: &_mesh_entry_point_name,
4814                        constants: &stage_desc.constants,
4815                        zero_initialize_workgroup_memory: stage_desc
4816                            .zero_initialize_workgroup_memory,
4817                    })
4818                };
4819            }
4820        }
4821
4822        let fragment_entry_point_name;
4823        let fragment_stage = match desc.fragment {
4824            Some(ref fragment_state) => {
4825                let stage = validation::ShaderStageForValidation::Fragment {
4826                    dual_source_blending,
4827                    has_depth_attachment,
4828                };
4829                let stage_bit = stage.to_wgt_bit();
4830
4831                let shader_module = &fragment_state.stage.module;
4832                shader_module.same_device(self)?;
4833
4834                let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4835                    stage: stage_bit,
4836                    error,
4837                };
4838
4839                fragment_entry_point_name = shader_module
4840                    .finalize_entry_point_name(
4841                        stage.to_naga(),
4842                        fragment_state
4843                            .stage
4844                            .entry_point
4845                            .as_ref()
4846                            .map(|ep| ep.as_ref()),
4847                    )
4848                    .map_err(stage_err)?;
4849
4850                if let Some(interface) = shader_module.interface.interface() {
4851                    immediate_slots_required |= interface
4852                        .immediate_slots_required(stage.to_naga(), &fragment_entry_point_name);
4853                    io = interface
4854                        .check_stage(
4855                            &mut binding_layout_source,
4856                            &mut shader_binding_sizes,
4857                            &fragment_entry_point_name,
4858                            stage,
4859                            io,
4860                            Some(desc.primitive.topology),
4861                        )
4862                        .map_err(stage_err)?;
4863                    validated_stages |= stage_bit;
4864                }
4865
4866                Some(hal::ProgrammableStage {
4867                    module: shader_module.raw(),
4868                    entry_point: &fragment_entry_point_name,
4869                    constants: &fragment_state.stage.constants,
4870                    zero_initialize_workgroup_memory: fragment_state
4871                        .stage
4872                        .zero_initialize_workgroup_memory,
4873                })
4874            }
4875            None => None,
4876        };
4877
4878        if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
4879            for (i, output) in io.varyings.iter() {
4880                match color_targets.get(*i as usize) {
4881                    Some(Some(state)) => {
4882                        validation::check_texture_format(state.format, &output.ty).map_err(
4883                            |pipeline| {
4884                                pipeline::CreateRenderPipelineError::ColorState(
4885                                    *i as u8,
4886                                    ColorStateError::IncompatibleFormat {
4887                                        pipeline,
4888                                        shader: output.ty,
4889                                    },
4890                                )
4891                            },
4892                        )?;
4893                    }
4894                    _ => {
4895                        log::debug!(
4896                            "The fragment stage {:?} output @location({}) values are ignored",
4897                            fragment_stage
4898                                .as_ref()
4899                                .map_or("", |stage| stage.entry_point),
4900                            i
4901                        );
4902                    }
4903                }
4904            }
4905        }
4906        let last_stage = match desc.fragment {
4907            Some(_) => wgt::ShaderStages::FRAGMENT,
4908            None => wgt::ShaderStages::VERTEX,
4909        };
4910        if is_auto_layout && !validated_stages.contains(last_stage) {
4911            return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
4912        }
4913
4914        let pipeline_layout = match binding_layout_source {
4915            validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
4916            validation::BindingLayoutSource::Derived(entries) => {
4917                let immediate_size = {
4918                    let immediate_size_of = |sm: &pipeline::ShaderModule| {
4919                        sm.interface.interface().map(|i| i.immediate_size)
4920                    };
4921                    let vertex = match desc.vertex {
4922                        pipeline::RenderPipelineVertexProcessor::Vertex(ref v) => {
4923                            immediate_size_of(&v.stage.module)
4924                        }
4925                        pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => task
4926                            .as_ref()
4927                            .and_then(|t| immediate_size_of(&t.stage.module))
4928                            .max(immediate_size_of(&mesh.stage.module)),
4929                    };
4930                    let fragment = desc
4931                        .fragment
4932                        .as_ref()
4933                        .and_then(|f| immediate_size_of(&f.stage.module));
4934                    vertex.max(fragment).unwrap_or(0)
4935                };
4936                self.create_derived_pipeline_layout(entries, immediate_size)?
4937            }
4938        };
4939
4940        if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
4941            let bind_groups_plus_vertex_buffers =
4942                u32::try_from(pipeline_layout.bind_group_layouts.len() + vertex.buffers.len())
4943                    .unwrap();
4944            if bind_groups_plus_vertex_buffers > self.limits.max_bind_groups_plus_vertex_buffers {
4945                return Err(
4946                    pipeline::CreateRenderPipelineError::TooManyBindGroupsPlusVertexBuffers {
4947                        given: bind_groups_plus_vertex_buffers,
4948                        limit: self.limits.max_bind_groups_plus_vertex_buffers,
4949                    },
4950                );
4951            }
4952
4953            let given = pipeline_layout
4954                .buffers_and_acceleration_structures_in_vertex_stage
4955                .saturating_add(vertex.buffers.len() as u32);
4956            if !self
4957                .instance_flags
4958                .contains(wgt::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
4959            {
4960                let limit = self
4961                    .limits
4962                    .max_buffers_and_acceleration_structures_per_shader_stage;
4963                if given > limit {
4964                    return Err(
4965                    pipeline::CreateRenderPipelineError::TooManyBuffersAndAccelerationStructuresInVertexStage {
4966                        given,
4967                        limit,
4968                    },
4969                );
4970                }
4971            }
4972        }
4973
4974        // Multiview is only supported if the feature is enabled
4975        if let Some(mv_mask) = desc.multiview_mask {
4976            self.require_features(wgt::Features::MULTIVIEW)?;
4977            if !(mv_mask.get() + 1).is_power_of_two() {
4978                self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
4979            }
4980        }
4981
4982        if !self
4983            .downlevel
4984            .flags
4985            .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
4986        {
4987            for (binding, size) in shader_binding_sizes.iter() {
4988                if size.get() % 16 != 0 {
4989                    return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
4990                        binding: binding.binding,
4991                        group: binding.group,
4992                        size: size.get(),
4993                    });
4994                }
4995            }
4996        }
4997
4998        let late_sized_buffer_groups =
4999            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
5000
5001        let cache = match desc.cache {
5002            Some(cache) => {
5003                cache.same_device(self)?;
5004                Some(cache)
5005            }
5006            None => None,
5007        };
5008
5009        let is_mesh = mesh_stage.is_some();
5010        let has_task_shader = task_stage.is_some();
5011        let raw = {
5012            let pipeline_desc = hal::RenderPipelineDescriptor {
5013                label: desc.label.to_hal(self.instance_flags),
5014                layout: pipeline_layout.raw()?,
5015                vertex_processor: match vertex_stage {
5016                    Some(vertex_stage) => hal::VertexProcessor::Standard {
5017                        vertex_buffers: &hal_vertex_buffer_layouts,
5018                        vertex_stage,
5019                    },
5020                    None => hal::VertexProcessor::Mesh {
5021                        task_stage,
5022                        mesh_stage: mesh_stage.unwrap(),
5023                    },
5024                },
5025                primitive: desc.primitive,
5026                depth_stencil: desc.depth_stencil.clone(),
5027                multisample: desc.multisample,
5028                fragment_stage,
5029                color_targets,
5030                multiview_mask: desc.multiview_mask,
5031                cache: cache.as_ref().map(|it| it.raw()),
5032            };
5033            unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
5034                |err| match err {
5035                    hal::PipelineError::Device(error) => {
5036                        pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))
5037                    }
5038                    hal::PipelineError::Linkage(stage, msg) => {
5039                        pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
5040                    }
5041                    hal::PipelineError::EntryPoint(stage) => {
5042                        pipeline::CreateRenderPipelineError::Internal {
5043                            stage: hal::auxil::map_naga_stage(stage),
5044                            error: ENTRYPOINT_FAILURE_ERROR.to_string(),
5045                        }
5046                    }
5047                    hal::PipelineError::PipelineConstants(stage, error) => {
5048                        pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }
5049                    }
5050                },
5051            )?
5052        };
5053
5054        let pass_context = RenderPassContext {
5055            attachments: AttachmentData {
5056                colors: color_targets
5057                    .iter()
5058                    .map(|state| state.as_ref().map(|s| s.format))
5059                    .collect(),
5060                resolves: ArrayVec::new(),
5061                depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
5062            },
5063            sample_count: samples,
5064            multiview_mask: desc.multiview_mask,
5065        };
5066
5067        let mut flags = pipeline::PipelineFlags::empty();
5068        for state in color_targets.iter().filter_map(|s| s.as_ref()) {
5069            if let Some(ref bs) = state.blend {
5070                if bs.color.uses_constant() | bs.alpha.uses_constant() {
5071                    flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
5072                }
5073            }
5074        }
5075        if let Some(ds) = depth_stencil_state.as_ref() {
5076            if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
5077                flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
5078            }
5079            if !ds.is_depth_read_only() {
5080                flags |= pipeline::PipelineFlags::WRITES_DEPTH;
5081            }
5082            if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
5083                flags |= pipeline::PipelineFlags::WRITES_STENCIL;
5084            }
5085        }
5086        let shader_modules = {
5087            let mut shader_modules = ArrayVec::new();
5088            match desc.vertex {
5089                pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
5090                    shader_modules.push(vertex.stage.module)
5091                }
5092                pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
5093                    if let Some(task) = task {
5094                        shader_modules.push(task.stage.module);
5095                    }
5096                    shader_modules.push(mesh.stage.module);
5097                }
5098            }
5099            shader_modules.extend(desc.fragment.map(|f| f.stage.module));
5100            shader_modules
5101        };
5102
5103        let pipeline = pipeline::RenderPipeline {
5104            state: ResourceState::Valid(pipeline::RenderPipelineState {
5105                raw: ManuallyDrop::new(raw),
5106                layout: pipeline_layout.clone(),
5107            }),
5108            device: self.clone(),
5109            pass_context,
5110            _shader_modules: shader_modules,
5111            flags,
5112            topology: desc.primitive.topology,
5113            strip_index_format: desc.primitive.strip_index_format,
5114            vertex_steps,
5115            late_sized_buffer_groups,
5116            immediate_slots_required,
5117            label: desc.label.to_string(),
5118            tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),
5119            is_mesh,
5120            has_task_shader,
5121        };
5122
5123        let pipeline = Arc::new(pipeline);
5124
5125        if is_auto_layout {
5126            for bgl in pipeline_layout.bind_group_layouts.iter() {
5127                let Some(bgl) = bgl else {
5128                    continue;
5129                };
5130
5131                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the
5132                // result.
5133                let _ = bgl.exclusive_pipeline.set((&pipeline).into());
5134            }
5135        }
5136
5137        Ok(pipeline)
5138    }
5139
5140    /// # Safety
5141    /// The `data` field on `desc` must have previously been returned from
5142    /// [`crate::global::Global::pipeline_cache_get_data`]
5143    pub unsafe fn create_pipeline_cache(
5144        self: &Arc<Self>,
5145        desc: &pipeline::PipelineCacheDescriptor,
5146    ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {
5147        use crate::pipeline_cache;
5148
5149        self.check_is_valid()?;
5150
5151        self.require_features(wgt::Features::PIPELINE_CACHE)?;
5152        let data = if let Some((data, validation_key)) = desc
5153            .data
5154            .as_ref()
5155            .zip(self.raw().pipeline_cache_validation_key())
5156        {
5157            let data = pipeline_cache::validate_pipeline_cache(
5158                data,
5159                &self.adapter.raw.info,
5160                validation_key,
5161            );
5162            match data {
5163                Ok(data) => Some(data),
5164                Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),
5165                // If the error was unavoidable and we are asked to fallback, do so
5166                Err(_) => None,
5167            }
5168        } else {
5169            None
5170        };
5171        let cache_desc = hal::PipelineCacheDescriptor {
5172            data,
5173            label: desc.label.to_hal(self.instance_flags),
5174        };
5175        let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {
5176            Ok(raw) => raw,
5177            Err(e) => match e {
5178                hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),
5179            },
5180        };
5181        let cache = pipeline::PipelineCache {
5182            device: self.clone(),
5183            label: desc.label.to_string(),
5184            // This would be none in the error condition, which we don't implement yet
5185            raw: ManuallyDrop::new(raw),
5186        };
5187
5188        let cache = Arc::new(cache);
5189
5190        Ok(cache)
5191    }
5192
5193    fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {
5194        // Variant of adapter.get_texture_format_features that takes device features into account
5195        use wgt::TextureFormatFeatureFlags as tfsc;
5196        let mut format_features = self.adapter.get_texture_format_features(format);
5197        if (format == TextureFormat::R32Float
5198            || format == TextureFormat::Rg32Float
5199            || format == TextureFormat::Rgba32Float)
5200            && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
5201        {
5202            format_features.flags.set(tfsc::FILTERABLE, false);
5203        }
5204        format_features
5205    }
5206
5207    pub(crate) fn describe_format_features(
5208        &self,
5209        format: TextureFormat,
5210    ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
5211        self.require_features(format.required_features())?;
5212
5213        let using_device_features = self
5214            .features
5215            .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
5216        // If we're running downlevel, we need to manually ask the backend what
5217        // we can use as we can't trust WebGPU.
5218        let downlevel = !self
5219            .downlevel
5220            .flags
5221            .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
5222
5223        if using_device_features || downlevel {
5224            Ok(self.get_texture_format_features(format))
5225        } else {
5226            Ok(format.guaranteed_format_features(self.features))
5227        }
5228    }
5229
5230    #[cfg(feature = "replay")]
5231    pub(crate) fn wait_for_submit(
5232        &self,
5233        submission_index: crate::SubmissionIndex,
5234    ) -> Result<(), DeviceError> {
5235        let last_done_index = unsafe { self.raw().get_fence_value(self.fence.as_ref()) }
5236            .map_err(|e| self.handle_hal_error(e))?;
5237        if last_done_index < submission_index {
5238            unsafe { self.raw().wait(self.fence.as_ref(), submission_index, None) }
5239                .map_err(|e| self.handle_hal_error(e))?;
5240            if let Some(queue) = self.get_queue() {
5241                let closures = queue.lock_life().triage_submissions(submission_index);
5242                assert!(
5243                    closures.is_empty(),
5244                    "wait_for_submit is not expected to work with closures"
5245                );
5246            }
5247        }
5248        Ok(())
5249    }
5250
5251    pub fn create_query_set(
5252        self: &Arc<Self>,
5253        desc: &resource::QuerySetDescriptor,
5254    ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {
5255        use resource::CreateQuerySetError as Error;
5256
5257        self.check_is_valid()?;
5258
5259        match desc.ty {
5260            wgt::QueryType::Occlusion => {}
5261            wgt::QueryType::Timestamp => {
5262                self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
5263            }
5264            wgt::QueryType::PipelineStatistics(..) => {
5265                self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
5266            }
5267        }
5268
5269        if desc.count == 0 {
5270            return Err(Error::ZeroCount);
5271        }
5272
5273        if desc.count > wgt::QUERY_SET_MAX_QUERIES {
5274            return Err(Error::TooManyQueries {
5275                count: desc.count,
5276                maximum: wgt::QUERY_SET_MAX_QUERIES,
5277            });
5278        }
5279
5280        let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
5281
5282        let raw = unsafe { self.raw().create_query_set(&hal_desc) }
5283            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
5284
5285        let query_set = QuerySet {
5286            raw: Snatchable::new(raw),
5287            device: self.clone(),
5288            label: desc.label.to_string(),
5289            tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),
5290            desc: desc.map_label(|_| ()),
5291            initialized_slots: Mutex::new(
5292                rank::QUERY_SET_INITIALIZED_SLOTS,
5293                bit_vec::BitVec::from_elem(desc.count as usize, false),
5294            ),
5295        };
5296
5297        let query_set = Arc::new(query_set);
5298
5299        Ok(query_set)
5300    }
5301
5302    pub fn configure_surface(
5303        self: &Arc<Self>,
5304        surface: &crate::instance::Surface,
5305        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
5306    ) -> Option<present::ConfigureSurfaceError> {
5307        use present::ConfigureSurfaceError as E;
5308        profiling::scope!("surface_configure");
5309
5310        log::debug!("configuring surface with {config:?}");
5311
5312        let error = 'error: {
5313            // User callbacks must not be called while we are holding locks.
5314            let user_callbacks;
5315            {
5316                if let Err(e) = self.check_is_valid() {
5317                    break 'error e.into();
5318                }
5319
5320                let caps = match surface.get_capabilities(&self.adapter) {
5321                    Ok(caps) => caps,
5322                    Err(_) => break 'error E::UnsupportedQueueFamily,
5323                };
5324
5325                let mut hal_view_formats = Vec::new();
5326                for format in config.view_formats.iter() {
5327                    if *format == config.format {
5328                        continue;
5329                    }
5330                    if !caps.formats.iter().any(|fc| fc.format == config.format) {
5331                        break 'error E::UnsupportedFormat {
5332                            requested: config.format,
5333                            available: caps.texture_formats().collect(),
5334                        };
5335                    }
5336                    if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
5337                        break 'error E::InvalidViewFormat(*format, config.format);
5338                    }
5339                    hal_view_formats.push(*format);
5340                }
5341
5342                if !hal_view_formats.is_empty() {
5343                    if let Err(missing_flag) =
5344                        self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
5345                    {
5346                        break 'error E::MissingDownlevelFlags(missing_flag);
5347                    }
5348                }
5349
5350                let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
5351                    *caps.maximum_frame_latency.start(),
5352                    *caps.maximum_frame_latency.end(),
5353                );
5354                let mut hal_config = hal::SurfaceConfiguration {
5355                    maximum_frame_latency,
5356                    present_mode: config.present_mode,
5357                    composite_alpha_mode: config.alpha_mode,
5358                    format: config.format,
5359                    color_space: config.color_space,
5360                    extent: wgt::Extent3d {
5361                        width: config.width,
5362                        height: config.height,
5363                        depth_or_array_layers: 1,
5364                    },
5365                    usage: conv::map_texture_usage(
5366                        config.usage,
5367                        hal::FormatAspects::COLOR,
5368                        wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
5369                            | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
5370                            | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
5371                    ),
5372                    view_formats: hal_view_formats,
5373                };
5374
5375                if let Err(error) = validate_surface_configuration(
5376                    &mut hal_config,
5377                    &caps,
5378                    self.limits.max_texture_dimension_2d,
5379                ) {
5380                    break 'error error;
5381                }
5382
5383                // Wait for all work to finish before configuring the surface.
5384                let snatch_guard = self.snatchable_lock.read();
5385
5386                let maintain_result;
5387                (user_callbacks, maintain_result) =
5388                    self.maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
5389
5390                match maintain_result {
5391                    // We're happy
5392                    Ok(wgt::PollStatus::QueueEmpty) => {}
5393                    Ok(wgt::PollStatus::WaitSucceeded) => {
5394                        // After the wait, the queue should be empty. It can only be non-empty
5395                        // if another thread is submitting at the same time.
5396                        break 'error E::GpuWaitTimeout;
5397                    }
5398                    Ok(wgt::PollStatus::Poll) => {
5399                        unreachable!("Cannot get a Poll result from a Wait action.")
5400                    }
5401                    Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
5402                        // On wasm, you cannot actually successfully wait for the surface.
5403                        // However WebGL does not actually require you do this, so ignoring
5404                        // the failure is totally fine. See
5405                        // https://github.com/gfx-rs/wgpu/issues/7363
5406                    }
5407                    Err(e) => {
5408                        break 'error e.into();
5409                    }
5410                }
5411
5412                // All textures must be destroyed before the surface can be re-configured.
5413                if let Some(present) = surface.presentation.lock().take() {
5414                    if present.acquired_texture.is_some() {
5415                        break 'error E::PreviousOutputExists;
5416                    }
5417                }
5418
5419                // TODO: Texture views may still be alive that point to the texture.
5420                // this will allow the user to render to the surface texture, long after
5421                // it has been removed.
5422                //
5423                // https://github.com/gfx-rs/wgpu/issues/4105
5424
5425                let surface_raw = surface.raw(self.backend()).unwrap();
5426                match unsafe { surface_raw.configure(self.raw(), &hal_config) } {
5427                    Ok(()) => (),
5428                    Err(error) => {
5429                        break 'error match error {
5430                            hal::SurfaceError::Outdated
5431                            | hal::SurfaceError::Lost
5432                            | hal::SurfaceError::Occluded
5433                            | hal::SurfaceError::Timeout => E::InvalidSurface,
5434                            hal::SurfaceError::Device(error) => {
5435                                E::Device(self.handle_hal_error(error))
5436                            }
5437                            hal::SurfaceError::Other(message) => {
5438                                log::error!("surface configuration failed: {message}");
5439                                E::InvalidSurface
5440                            }
5441                        }
5442                    }
5443                }
5444
5445                let mut presentation = surface.presentation.lock();
5446                *presentation = Some(present::Presentation {
5447                    device: Arc::clone(self),
5448                    config: config.clone(),
5449                    acquired_texture: None,
5450                });
5451            }
5452
5453            user_callbacks.fire();
5454            return None;
5455        };
5456
5457        Some(error)
5458    }
5459
5460    fn lose(&self, message: &str) {
5461        // Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device.
5462
5463        // Mark the device explicitly as invalid. This is checked in various
5464        // places to prevent new work from being submitted.
5465        self.valid.store(false, Ordering::Release);
5466
5467        // 1) Resolve the GPUDevice device.lost promise.
5468        if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
5469            device_lost_closure(DeviceLostReason::Unknown, message.to_string());
5470        }
5471
5472        // 2) Complete any outstanding mapAsync() steps.
5473        // 3) Complete any outstanding onSubmittedWorkDone() steps.
5474
5475        // These parts are passively accomplished by setting valid to false,
5476        // since that will prevent any new work from being added to the queues.
5477        // Future calls to poll_devices will continue to check the work queues
5478        // until they are cleared, and then drop the device.
5479    }
5480
5481    fn release_gpu_resources(&self) {
5482        // This is called when the device is lost, which makes every associated
5483        // resource invalid and unusable. This is an opportunity to release all of
5484        // the underlying gpu resources, even though the objects remain visible to
5485        // the user agent. We purge this memory naturally when resources have been
5486        // moved into the appropriate buckets, so this function just needs to
5487        // initiate movement into those buckets, and it can do that by calling
5488        // "destroy" on all the resources we know about.
5489
5490        // During these iterations, we discard all errors. We don't care!
5491        let trackers = self.trackers.lock();
5492        for buffer in trackers.buffers.used_resources() {
5493            if let Some(buffer) = Weak::upgrade(buffer) {
5494                buffer.destroy();
5495            }
5496        }
5497        for texture in trackers.textures.used_resources() {
5498            if let Some(texture) = Weak::upgrade(texture) {
5499                texture.destroy();
5500            }
5501        }
5502    }
5503
5504    pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {
5505        UsageScope::new_pooled(
5506            &self.usage_scopes,
5507            &self.tracker_indices,
5508            self.ordered_buffer_usages,
5509            self.ordered_texture_usages,
5510        )
5511    }
5512
5513    pub fn get_hal_counters(&self) -> wgt::HalCounters {
5514        self.raw().get_internal_counters()
5515    }
5516
5517    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
5518        self.raw().generate_allocator_report()
5519    }
5520}
5521
5522crate::impl_resource_type!(Device);
5523crate::impl_labeled!(Device);
5524crate::impl_storage_item!(Device);