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    #[inline(never)]
3301    fn create_external_texture_binding<'a>(
3302        &'a self,
3303        binding: u32,
3304        decl: &wgt::BindGroupLayoutEntry,
3305        external_texture: &'a Arc<ExternalTexture>,
3306        used: &mut BindGroupStates,
3307        snatch_guard: &'a SnatchGuard,
3308    ) -> Result<
3309        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3310        CreateBindGroupError,
3311    > {
3312        use crate::binding_model::CreateBindGroupError as Error;
3313
3314        external_texture.same_device(self)?;
3315
3316        used.external_textures
3317            .insert_single(external_texture.clone());
3318
3319        match decl.ty {
3320            wgt::BindingType::ExternalTexture => {}
3321            _ => {
3322                return Err(Error::WrongBindingType {
3323                    binding,
3324                    actual: decl.ty,
3325                    expected: "ExternalTexture",
3326                });
3327            }
3328        }
3329
3330        let planes = (0..3)
3331            .map(|i| {
3332                // We always need 3 bindings. If we have fewer than 3 planes
3333                // just bind plane 0 multiple times. The shader will only
3334                // sample from valid planes anyway.
3335                let plane = external_texture
3336                    .planes
3337                    .get(i)
3338                    .unwrap_or(&external_texture.planes[0]);
3339                let internal_use = wgt::TextureUses::RESOURCE;
3340                used.views.insert_single(plane.clone(), internal_use);
3341                let view = plane.try_raw(snatch_guard)?;
3342                Ok(hal::TextureBinding {
3343                    view,
3344                    usage: internal_use,
3345                })
3346            })
3347            // We can remove this intermediate Vec by using
3348            // array::try_from_fn() above, once it stabilizes.
3349            .collect::<Result<Vec<_>, Error>>()?;
3350        let planes = planes.try_into().unwrap();
3351
3352        used.buffers
3353            .insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);
3354        let params = external_texture.params.binding(0, None, snatch_guard)?.0;
3355
3356        Ok(hal::ExternalTextureBinding { planes, params })
3357    }
3358
3359    fn create_external_texture_binding_from_view<'a>(
3360        &'a self,
3361        binding: u32,
3362        decl: &wgt::BindGroupLayoutEntry,
3363        view: &'a Arc<TextureView>,
3364        used: &mut BindGroupStates,
3365        snatch_guard: &'a SnatchGuard,
3366    ) -> Result<
3367        hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3368        CreateBindGroupError,
3369    > {
3370        use crate::binding_model::CreateBindGroupError as Error;
3371
3372        view.same_device(self)?;
3373
3374        let internal_use = self.texture_use_parameters(binding, decl, view, "SampledTexture")?;
3375        used.views.insert_single(view.clone(), internal_use);
3376
3377        match decl.ty {
3378            wgt::BindingType::ExternalTexture => {}
3379            _ => {
3380                return Err(Error::WrongBindingType {
3381                    binding,
3382                    actual: decl.ty,
3383                    expected: "ExternalTexture",
3384                });
3385            }
3386        }
3387
3388        // We need 3 bindings, so just repeat the same texture view 3 times.
3389        let planes = [
3390            hal::TextureBinding {
3391                view: view.try_raw(snatch_guard)?,
3392                usage: internal_use,
3393            },
3394            hal::TextureBinding {
3395                view: view.try_raw(snatch_guard)?,
3396                usage: internal_use,
3397            },
3398            hal::TextureBinding {
3399                view: view.try_raw(snatch_guard)?,
3400                usage: internal_use,
3401            },
3402        ];
3403        let params = hal::BufferBinding::new_unchecked(
3404            self.default_external_texture_params_buffer.as_ref(),
3405            0,
3406            None,
3407        );
3408
3409        Ok(hal::ExternalTextureBinding { planes, params })
3410    }
3411
3412    // This function expects the provided bind group layout to be resolved
3413    // (not passing a duplicate) beforehand.
3414    pub fn create_bind_group(
3415        self: &Arc<Self>,
3416        desc: binding_model::ResolvedBindGroupDescriptor,
3417    ) -> Result<Arc<BindGroup>, CreateBindGroupError> {
3418        use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};
3419
3420        let layout = desc.layout;
3421
3422        self.check_is_valid()?;
3423        layout.same_device(self)?;
3424        layout.check_is_valid()?;
3425
3426        {
3427            // Check that the number of entries in the descriptor matches
3428            // the number of entries in the layout.
3429            let actual = desc.entries.len();
3430            let expected = layout.entries.len();
3431            if actual != expected {
3432                return Err(Error::BindingsNumMismatch { expected, actual });
3433            }
3434        }
3435
3436        // TODO: arrayvec/smallvec, or re-use allocations
3437        // Record binding info for dynamic offset validation
3438        let mut dynamic_binding_info = Vec::new();
3439        // Map of binding -> shader reflected size
3440        //Note: we can't collect into a vector right away because
3441        // it needs to be in BGL iteration order, not BG entry order.
3442        let mut late_buffer_binding_sizes = FastHashMap::default();
3443        // fill out the descriptors
3444        let mut used = BindGroupStates::new();
3445
3446        let mut buffer_init_actions = Vec::new();
3447        let mut texture_init_actions = Vec::new();
3448        let mut hal_entries = Vec::with_capacity(desc.entries.len());
3449        let mut hal_buffers = Vec::new();
3450        let mut hal_samplers = Vec::new();
3451        let mut hal_textures = Vec::new();
3452        let mut hal_tlas_s = Vec::new();
3453        let mut hal_external_textures = Vec::new();
3454        let snatch_guard = self.snatchable_lock.read();
3455        for entry in desc.entries.iter() {
3456            let binding = entry.binding;
3457            // Find the corresponding declaration in the layout
3458            let decl = layout
3459                .entries
3460                .get(binding)
3461                .ok_or(Error::MissingBindingDeclaration(binding))?;
3462            let (res_index, count) = match entry.resource {
3463                Br::Buffer(ref bb) => {
3464                    let bb = self.create_buffer_binding(
3465                        bb,
3466                        binding,
3467                        decl,
3468                        &mut buffer_init_actions,
3469                        &mut dynamic_binding_info,
3470                        &mut late_buffer_binding_sizes,
3471                        &mut used,
3472                        &snatch_guard,
3473                    )?;
3474
3475                    let res_index = hal_buffers.len();
3476                    hal_buffers.push(bb);
3477                    (res_index, 1)
3478                }
3479                Br::BufferArray(ref bindings_array) => {
3480                    let num_bindings = bindings_array.len();
3481                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3482
3483                    let res_index = hal_buffers.len();
3484                    for bb in bindings_array.iter() {
3485                        let bb = self.create_buffer_binding(
3486                            bb,
3487                            binding,
3488                            decl,
3489                            &mut buffer_init_actions,
3490                            &mut dynamic_binding_info,
3491                            &mut late_buffer_binding_sizes,
3492                            &mut used,
3493                            &snatch_guard,
3494                        )?;
3495                        hal_buffers.push(bb);
3496                    }
3497                    (res_index, num_bindings)
3498                }
3499                Br::Sampler(ref sampler) => {
3500                    let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3501
3502                    let res_index = hal_samplers.len();
3503                    hal_samplers.push(sampler);
3504                    (res_index, 1)
3505                }
3506                Br::SamplerArray(ref samplers) => {
3507                    let num_bindings = samplers.len();
3508                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3509
3510                    let res_index = hal_samplers.len();
3511                    for sampler in samplers.iter() {
3512                        let sampler =
3513                            self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3514
3515                        hal_samplers.push(sampler);
3516                    }
3517
3518                    (res_index, num_bindings)
3519                }
3520                Br::TextureView(ref view) => match decl.ty {
3521                    wgt::BindingType::ExternalTexture => {
3522                        let et = self.create_external_texture_binding_from_view(
3523                            binding,
3524                            decl,
3525                            view,
3526                            &mut used,
3527                            &snatch_guard,
3528                        )?;
3529                        let res_index = hal_external_textures.len();
3530                        hal_external_textures.push(et);
3531                        (res_index, 1)
3532                    }
3533                    _ => {
3534                        let tb = self.create_texture_binding(
3535                            binding,
3536                            decl,
3537                            view,
3538                            &mut used,
3539                            &mut texture_init_actions,
3540                            &snatch_guard,
3541                        )?;
3542                        let res_index = hal_textures.len();
3543                        hal_textures.push(tb);
3544                        (res_index, 1)
3545                    }
3546                },
3547                Br::TextureViewArray(ref views) => {
3548                    let num_bindings = views.len();
3549                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3550
3551                    let res_index = hal_textures.len();
3552                    for view in views.iter() {
3553                        let tb = self.create_texture_binding(
3554                            binding,
3555                            decl,
3556                            view,
3557                            &mut used,
3558                            &mut texture_init_actions,
3559                            &snatch_guard,
3560                        )?;
3561
3562                        hal_textures.push(tb);
3563                    }
3564
3565                    (res_index, num_bindings)
3566                }
3567                Br::AccelerationStructure(ref tlas) => {
3568                    let tlas =
3569                        self.create_tlas_binding(&mut used, binding, decl, tlas, &snatch_guard)?;
3570                    let res_index = hal_tlas_s.len();
3571                    hal_tlas_s.push(tlas);
3572                    (res_index, 1)
3573                }
3574                Br::AccelerationStructureArray(ref tlas_array) => {
3575                    // Feature validation for TLAS binding arrays happens at bind group layout
3576                    // creation time (mirroring other binding-array resource types). By the time we
3577                    // get here, `decl.count` has already been validated against device features.
3578                    let num_bindings = tlas_array.len();
3579                    Self::check_array_binding(self.features, decl.count, num_bindings)?;
3580
3581                    let res_index = hal_tlas_s.len();
3582                    for tlas in tlas_array.iter() {
3583                        let tlas = self.create_tlas_binding(
3584                            &mut used,
3585                            binding,
3586                            decl,
3587                            tlas,
3588                            &snatch_guard,
3589                        )?;
3590                        hal_tlas_s.push(tlas);
3591                    }
3592                    (res_index, num_bindings)
3593                }
3594                Br::ExternalTexture(ref et) => {
3595                    let et = self.create_external_texture_binding(
3596                        binding,
3597                        decl,
3598                        et,
3599                        &mut used,
3600                        &snatch_guard,
3601                    )?;
3602                    let res_index = hal_external_textures.len();
3603                    hal_external_textures.push(et);
3604                    (res_index, 1)
3605                }
3606            };
3607
3608            hal_entries.push(hal::BindGroupEntry {
3609                binding,
3610                resource_index: res_index as u32,
3611                count: count as u32,
3612            });
3613        }
3614
3615        used.optimize();
3616
3617        hal_entries.sort_by_key(|entry| entry.binding);
3618        for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
3619            if a.binding == b.binding {
3620                return Err(Error::DuplicateBinding(a.binding));
3621            }
3622        }
3623
3624        dynamic_binding_info.sort_by_key(|i| i.binding_idx);
3625
3626        let hal_desc = hal::BindGroupDescriptor {
3627            label: desc.label.to_hal(self.instance_flags),
3628            layout: layout.try_raw()?,
3629            entries: &hal_entries,
3630            buffers: &hal_buffers,
3631            samplers: &hal_samplers,
3632            textures: &hal_textures,
3633            acceleration_structures: &hal_tlas_s,
3634            external_textures: &hal_external_textures,
3635        };
3636        let raw = unsafe { self.raw().create_bind_group(&hal_desc) }
3637            .map_err(|e| self.handle_hal_error(e))?;
3638
3639        // collect in the order of BGL iteration
3640        let late_buffer_binding_infos = layout
3641            .entries
3642            .indices()
3643            .flat_map(|binding| {
3644                let size = late_buffer_binding_sizes.get(&binding).cloned()?;
3645                Some(BindGroupLateBufferBindingInfo {
3646                    binding_index: binding,
3647                    size,
3648                })
3649            })
3650            .collect();
3651
3652        let bind_group = BindGroup {
3653            raw: Snatchable::new(raw),
3654            device: self.clone(),
3655            layout,
3656            label: desc.label.to_string(),
3657            tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),
3658            used,
3659            buffer_init_actions,
3660            texture_init_actions,
3661            dynamic_binding_info,
3662            late_buffer_binding_infos,
3663        };
3664
3665        let bind_group = Arc::new(bind_group);
3666
3667        let weak_ref = Arc::downgrade(&bind_group);
3668        for texture in bind_group.used.views.used_textures() {
3669            let mut bind_groups = texture.bind_groups.lock();
3670            bind_groups.push(weak_ref.clone());
3671        }
3672        for buffer in bind_group.used.buffers.used_resources() {
3673            let mut bind_groups = buffer.bind_groups.lock();
3674            bind_groups.push(weak_ref.clone());
3675        }
3676
3677        Ok(bind_group)
3678    }
3679
3680    fn check_array_binding(
3681        features: wgt::Features,
3682        count: Option<NonZeroU32>,
3683        num_bindings: usize,
3684    ) -> Result<(), CreateBindGroupError> {
3685        use super::binding_model::CreateBindGroupError as Error;
3686
3687        if let Some(count) = count {
3688            let count = count.get() as usize;
3689            if count < num_bindings {
3690                return Err(Error::BindingArrayPartialLengthMismatch {
3691                    actual: num_bindings,
3692                    expected: count,
3693                });
3694            }
3695            if count != num_bindings
3696                && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
3697            {
3698                return Err(Error::BindingArrayLengthMismatch {
3699                    actual: num_bindings,
3700                    expected: count,
3701                });
3702            }
3703            if num_bindings == 0 {
3704                return Err(Error::BindingArrayZeroLength);
3705            }
3706        } else {
3707            return Err(Error::SingleBindingExpected);
3708        };
3709
3710        Ok(())
3711    }
3712
3713    fn texture_use_parameters(
3714        &self,
3715        binding: u32,
3716        decl: &wgt::BindGroupLayoutEntry,
3717        view: &TextureView,
3718        expected: &'static str,
3719    ) -> Result<wgt::TextureUses, CreateBindGroupError> {
3720        use crate::binding_model::CreateBindGroupError as Error;
3721        if view
3722            .desc
3723            .aspects()
3724            .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
3725        {
3726            return Err(Error::DepthStencilAspect);
3727        }
3728        match decl.ty {
3729            wgt::BindingType::Texture {
3730                sample_type,
3731                view_dimension,
3732                multisampled,
3733            } => {
3734                use wgt::TextureSampleType as Tst;
3735                if multisampled != (view.samples != 1) {
3736                    return Err(Error::InvalidTextureMultisample {
3737                        binding,
3738                        layout_multisampled: multisampled,
3739                        view_samples: view.samples,
3740                    });
3741                }
3742                let compat_sample_type = view
3743                    .desc
3744                    .format
3745                    .sample_type(Some(view.desc.range.aspect), Some(self.features))
3746                    .unwrap();
3747                match (sample_type, compat_sample_type) {
3748                    (Tst::Uint, Tst::Uint) |
3749                        (Tst::Sint, Tst::Sint) |
3750                        (Tst::Depth, Tst::Depth) |
3751                        // if we expect non-filterable, accept anything float
3752                        (Tst::Float { filterable: false }, Tst::Float { .. }) |
3753                        // if we expect filterable, require it
3754                        (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
3755                        // if we expect non-filterable, also accept depth
3756                        (Tst::Float { filterable: false }, Tst::Depth) => {}
3757                    // if we expect filterable, also accept Float that is defined as
3758                    // unfilterable if filterable feature is explicitly enabled (only hit
3759                    // if wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES is
3760                    // enabled)
3761                    (Tst::Float { filterable: true }, Tst::Float { .. })
3762                        if view.format_features.flags
3763                            .contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
3764                    _ => {
3765                        return Err(Error::InvalidTextureSampleType {
3766                            binding,
3767                            layout_sample_type: sample_type,
3768                            view_format: view.desc.format,
3769                            view_sample_type: compat_sample_type,
3770                        })
3771                    }
3772                }
3773                if view_dimension != view.desc.dimension {
3774                    return Err(Error::InvalidTextureDimension {
3775                        binding,
3776                        layout_dimension: view_dimension,
3777                        view_dimension: view.desc.dimension,
3778                    });
3779                }
3780                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3781                Ok(wgt::TextureUses::RESOURCE)
3782            }
3783            wgt::BindingType::StorageTexture {
3784                access,
3785                format,
3786                view_dimension,
3787            } => {
3788                if format != view.desc.format {
3789                    return Err(Error::InvalidStorageTextureFormat {
3790                        binding,
3791                        layout_format: format,
3792                        view_format: view.desc.format,
3793                    });
3794                }
3795                if view_dimension != view.desc.dimension {
3796                    return Err(Error::InvalidTextureDimension {
3797                        binding,
3798                        layout_dimension: view_dimension,
3799                        view_dimension: view.desc.dimension,
3800                    });
3801                }
3802
3803                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3804                if mip_level_count != 1 {
3805                    return Err(Error::InvalidStorageTextureMipLevelCount {
3806                        binding,
3807                        mip_level_count,
3808                    });
3809                }
3810
3811                view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;
3812
3813                Ok(match access {
3814                    wgt::StorageTextureAccess::ReadOnly => wgt::TextureUses::STORAGE_READ_ONLY,
3815                    wgt::StorageTextureAccess::WriteOnly => wgt::TextureUses::STORAGE_WRITE_ONLY,
3816                    wgt::StorageTextureAccess::ReadWrite => wgt::TextureUses::STORAGE_READ_WRITE,
3817                    wgt::StorageTextureAccess::Atomic => wgt::TextureUses::STORAGE_ATOMIC,
3818                })
3819            }
3820            wgt::BindingType::ExternalTexture => {
3821                if view.desc.dimension != TextureViewDimension::D2 {
3822                    return Err(Error::InvalidTextureDimension {
3823                        binding,
3824                        layout_dimension: TextureViewDimension::D2,
3825                        view_dimension: view.desc.dimension,
3826                    });
3827                }
3828                let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3829                if mip_level_count != 1 {
3830                    return Err(Error::InvalidExternalTextureMipLevelCount {
3831                        binding,
3832                        mip_level_count,
3833                    });
3834                }
3835                if view.desc.format != TextureFormat::Rgba8Unorm
3836                    && view.desc.format != TextureFormat::Bgra8Unorm
3837                    && view.desc.format != TextureFormat::Rgba16Float
3838                {
3839                    return Err(Error::InvalidExternalTextureFormat {
3840                        binding,
3841                        format: view.desc.format,
3842                    });
3843                }
3844                if view.samples != 1 {
3845                    return Err(Error::InvalidTextureMultisample {
3846                        binding,
3847                        layout_multisampled: false,
3848                        view_samples: view.samples,
3849                    });
3850                }
3851
3852                view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3853                Ok(wgt::TextureUses::RESOURCE)
3854            }
3855            _ => Err(Error::WrongBindingType {
3856                binding,
3857                actual: decl.ty,
3858                expected,
3859            }),
3860        }
3861    }
3862
3863    pub fn create_pipeline_layout(
3864        self: &Arc<Self>,
3865        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3866    ) -> (
3867        Arc<binding_model::PipelineLayout>,
3868        Option<binding_model::CreatePipelineLayoutError>,
3869    ) {
3870        let (layout, error) = match self.create_pipeline_layout_impl(desc, false) {
3871            Ok(layout) => (layout, None),
3872            Err(e) => (
3873                binding_model::PipelineLayout::invalid(Arc::clone(self), desc.label.to_string()),
3874                Some(e),
3875            ),
3876        };
3877        #[cfg(feature = "trace")]
3878        if let Some(ref mut trace) = *self.trace.lock() {
3879            use crate::device::trace::IntoTrace;
3880            trace.add(trace::Action::CreatePipelineLayout(
3881                layout.to_trace(),
3882                desc.to_trace(),
3883            ));
3884        }
3885        api_log!(
3886            "Device::create_pipeline_layout -> {:?}",
3887            Arc::as_ptr(&layout)
3888        );
3889        (layout, error)
3890    }
3891
3892    fn create_pipeline_layout_impl(
3893        self: &Arc<Self>,
3894        desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3895        ignore_exclusive_pipeline_check: bool,
3896    ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
3897        use crate::binding_model::CreatePipelineLayoutError as Error;
3898
3899        self.check_is_valid()?;
3900
3901        let bind_group_layouts_count = desc.bind_group_layouts.len();
3902        let device_max_bind_groups = self.limits.max_bind_groups as usize;
3903        if bind_group_layouts_count > device_max_bind_groups {
3904            return Err(Error::TooManyGroups {
3905                actual: bind_group_layouts_count,
3906                max: device_max_bind_groups,
3907            });
3908        }
3909
3910        if desc.immediate_size != 0 {
3911            self.require_features(wgt::Features::IMMEDIATES)?;
3912        }
3913        if self.limits.max_immediate_size < desc.immediate_size {
3914            return Err(Error::ImmediateRangeTooLarge {
3915                size: desc.immediate_size,
3916                max: self.limits.max_immediate_size,
3917            });
3918        }
3919        if !desc
3920            .immediate_size
3921            .is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT)
3922        {
3923            return Err(Error::MisalignedImmediateSize {
3924                size: desc.immediate_size,
3925            });
3926        }
3927
3928        let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
3929
3930        for (index, bgl) in desc.bind_group_layouts.iter().enumerate() {
3931            let Some(bgl) = bgl else {
3932                continue;
3933            };
3934
3935            bgl.same_device(self)?;
3936
3937            if !ignore_exclusive_pipeline_check {
3938                let exclusive_pipeline = bgl.exclusive_pipeline.get().unwrap();
3939                if !matches!(exclusive_pipeline, binding_model::ExclusivePipeline::None) {
3940                    return Err(Error::BglHasExclusivePipeline {
3941                        index,
3942                        pipeline: alloc::format!("{exclusive_pipeline}"),
3943                    });
3944                }
3945            }
3946
3947            count_validator.merge(&bgl.state()?.binding_count_validator);
3948        }
3949
3950        count_validator
3951            .validate(&self.limits, self.instance_flags)
3952            .map_err(Error::TooManyBindings)?;
3953
3954        let buffers_and_acceleration_structures_in_vertex_stage =
3955            count_validator.buffers_and_acceleration_structures_in_vertex_stage();
3956
3957        let get_bgl_iter = || {
3958            desc.bind_group_layouts
3959                .iter()
3960                .map(|bgl| bgl.as_ref().filter(|bgl| !bgl.entries.is_empty()))
3961        };
3962
3963        let bind_group_layouts = get_bgl_iter()
3964            .map(|bgl| bgl.cloned())
3965            .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3966
3967        let raw_bind_group_layouts = get_bgl_iter()
3968            .map(|bgl| bgl.map(|bgl| bgl.try_raw()).transpose())
3969            .collect::<Result<ArrayVec<_, { hal::MAX_BIND_GROUPS }>, _>>()?;
3970
3971        let additional_flags = if self.indirect_validation.is_some() {
3972            hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE
3973        } else {
3974            hal::PipelineLayoutFlags::empty()
3975        };
3976
3977        let hal_desc = hal::PipelineLayoutDescriptor {
3978            label: desc.label.to_hal(self.instance_flags),
3979            flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
3980                | hal::PipelineLayoutFlags::NUM_WORK_GROUPS
3981                | additional_flags,
3982            bind_group_layouts: &raw_bind_group_layouts,
3983            immediate_size: desc.immediate_size,
3984        };
3985
3986        let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }
3987            .map_err(|e| self.handle_hal_error(e))?;
3988
3989        drop(raw_bind_group_layouts);
3990
3991        let layout = binding_model::PipelineLayout {
3992            raw: ResourceState::Valid(raw),
3993            device: self.clone(),
3994            label: desc.label.to_string(),
3995            bind_group_layouts,
3996            immediate_size: desc.immediate_size,
3997            buffers_and_acceleration_structures_in_vertex_stage,
3998        };
3999
4000        let layout = Arc::new(layout);
4001
4002        Ok(layout)
4003    }
4004
4005    fn create_derived_pipeline_layout(
4006        self: &Arc<Self>,
4007        mut derived_group_layouts: Box<ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>>,
4008        immediate_size: u32,
4009    ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {
4010        while derived_group_layouts
4011            .last()
4012            .is_some_and(|map| map.is_empty())
4013        {
4014            derived_group_layouts.pop();
4015        }
4016
4017        let mut unique_bind_group_layouts = FastHashMap::default();
4018
4019        let bind_group_layouts = derived_group_layouts
4020            .into_iter()
4021            .map(|mut bgl_entry_map| {
4022                if bgl_entry_map.is_empty() {
4023                    return Ok(None);
4024                }
4025
4026                bgl_entry_map.sort();
4027                match unique_bind_group_layouts.entry(bgl_entry_map) {
4028                    hashbrown::hash_map::Entry::Occupied(v) => Ok(Some(Arc::clone(v.get()))),
4029                    hashbrown::hash_map::Entry::Vacant(e) => {
4030                        match self.create_bind_group_layout_impl(
4031                            &None,
4032                            e.key().clone(),
4033                            bgl::Origin::Derived,
4034                        ) {
4035                            Ok(bgl) => {
4036                                e.insert(bgl.clone());
4037                                Ok(Some(bgl))
4038                            }
4039                            Err(e) => Err(e),
4040                        }
4041                    }
4042                }
4043            })
4044            .collect::<Result<Vec<_>, _>>()?;
4045
4046        let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {
4047            label: None,
4048            bind_group_layouts: Cow::Owned(bind_group_layouts),
4049            immediate_size,
4050        };
4051
4052        let layout = self.create_pipeline_layout_impl(&layout_desc, true)?;
4053        Ok(layout)
4054    }
4055
4056    pub fn create_compute_pipeline(
4057        self: &Arc<Self>,
4058        desc: pipeline::ResolvedComputePipelineDescriptor,
4059    ) -> (
4060        Arc<pipeline::ComputePipeline>,
4061        Option<pipeline::CreateComputePipelineError>,
4062    ) {
4063        let (compute_pipeline, error) = match self.create_compute_pipeline_inner(desc.clone()) {
4064            Ok(compute_pipeline) => (compute_pipeline, None),
4065            Err(error) => (
4066                pipeline::ComputePipeline::invalid(self.clone(), desc.label.to_string()),
4067                Some(error),
4068            ),
4069        };
4070        #[cfg(feature = "trace")]
4071        if let Some(ref mut trace) = *self.trace.lock() {
4072            use crate::device::trace;
4073            use crate::device::trace::IntoTrace;
4074            trace.add(trace::Action::CreateComputePipeline {
4075                id: compute_pipeline.to_trace(),
4076                desc: desc.to_trace(),
4077            });
4078        }
4079        (compute_pipeline, error)
4080    }
4081
4082    pub fn create_compute_pipeline_inner(
4083        self: &Arc<Self>,
4084        desc: pipeline::ResolvedComputePipelineDescriptor,
4085    ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {
4086        self.check_is_valid()?;
4087
4088        self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
4089
4090        let shader_module = desc.stage.module;
4091
4092        shader_module.same_device(self)?;
4093
4094        let is_auto_layout = desc.layout.is_none();
4095
4096        // Get the pipeline layout from the desc if it is provided.
4097        let pipeline_layout = match desc.layout {
4098            Some(pipeline_layout) => {
4099                pipeline_layout.same_device(self)?;
4100                pipeline_layout.check_valid()?;
4101                Some(pipeline_layout)
4102            }
4103            None => None,
4104        };
4105
4106        let mut binding_layout_source = match pipeline_layout {
4107            Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4108            None => validation::BindingLayoutSource::new_derived(&self.limits),
4109        };
4110        let mut shader_binding_sizes = FastHashMap::default();
4111        let io = validation::StageIo::default();
4112
4113        let final_entry_point_name;
4114
4115        {
4116            let stage = validation::ShaderStageForValidation::Compute;
4117
4118            final_entry_point_name = shader_module.finalize_entry_point_name(
4119                stage.to_naga(),
4120                desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
4121            )?;
4122
4123            if let Some(interface) = shader_module.interface.interface() {
4124                let _ = interface.check_stage(
4125                    &mut binding_layout_source,
4126                    &mut shader_binding_sizes,
4127                    &final_entry_point_name,
4128                    stage,
4129                    io,
4130                    None,
4131                )?;
4132            }
4133        }
4134
4135        let pipeline_layout = match binding_layout_source {
4136            validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
4137            validation::BindingLayoutSource::Derived(entries) => {
4138                let immediate_size = shader_module
4139                    .interface
4140                    .interface()
4141                    .map_or(0, |i| i.immediate_size);
4142                self.create_derived_pipeline_layout(entries, immediate_size)?
4143            }
4144        };
4145
4146        let late_sized_buffer_groups =
4147            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
4148
4149        let cache = match desc.cache {
4150            Some(cache) => {
4151                cache.same_device(self)?;
4152                Some(cache)
4153            }
4154            None => None,
4155        };
4156
4157        let pipeline_desc = hal::ComputePipelineDescriptor {
4158            label: desc.label.to_hal(self.instance_flags),
4159            layout: pipeline_layout.raw()?,
4160            stage: hal::ProgrammableStage {
4161                module: shader_module.raw(),
4162                entry_point: final_entry_point_name.as_ref(),
4163                constants: &desc.stage.constants,
4164                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
4165            },
4166            cache: cache.as_ref().map(|it| it.raw()),
4167        };
4168
4169        let raw =
4170            unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(
4171                |err| match err {
4172                    hal::PipelineError::Device(error) => {
4173                        pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))
4174                    }
4175                    hal::PipelineError::Linkage(_stages, msg) => {
4176                        pipeline::CreateComputePipelineError::Internal(msg)
4177                    }
4178                    hal::PipelineError::EntryPoint(_stage) => {
4179                        pipeline::CreateComputePipelineError::Internal(
4180                            ENTRYPOINT_FAILURE_ERROR.to_string(),
4181                        )
4182                    }
4183                    hal::PipelineError::PipelineConstants(_stages, msg) => {
4184                        pipeline::CreateComputePipelineError::PipelineConstants(msg)
4185                    }
4186                },
4187            )?;
4188
4189        let immediate_slots_required =
4190            shader_module
4191                .interface
4192                .interface()
4193                .map_or(Default::default(), |iface| {
4194                    iface.immediate_slots_required(
4195                        naga::ShaderStage::Compute,
4196                        &final_entry_point_name,
4197                    )
4198                });
4199
4200        let pipeline = pipeline::ComputePipeline {
4201            state: ResourceState::Valid(pipeline::ComputePipelineState {
4202                raw: ManuallyDrop::new(raw),
4203                layout: pipeline_layout.clone(),
4204                _shader_module: shader_module,
4205            }),
4206            device: self.clone(),
4207            late_sized_buffer_groups,
4208            immediate_slots_required,
4209            label: desc.label.to_string(),
4210            tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),
4211        };
4212
4213        let pipeline = Arc::new(pipeline);
4214
4215        if is_auto_layout {
4216            for bgl in pipeline_layout.bind_group_layouts.iter() {
4217                let Some(bgl) = bgl else {
4218                    continue;
4219                };
4220
4221                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the
4222                // result.
4223                let _ = bgl.exclusive_pipeline.set((&pipeline).into());
4224            }
4225        }
4226
4227        Ok(pipeline)
4228    }
4229
4230    pub fn create_render_pipeline(
4231        self: &Arc<Self>,
4232        desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
4233    ) -> (
4234        Arc<pipeline::RenderPipeline>,
4235        Option<pipeline::CreateRenderPipelineError>,
4236    ) {
4237        let (render_pipeline, error) = match self.create_render_pipeline_inner(desc.clone()) {
4238            Ok(pipeline) => (pipeline, None),
4239            Err(e) => (
4240                pipeline::RenderPipeline::invalid(self.clone(), desc.label.to_string()),
4241                Some(e),
4242            ),
4243        };
4244        #[cfg(feature = "trace")]
4245        if let Some(ref mut trace) = *self.trace.lock() {
4246            use crate::device::trace::IntoTrace;
4247            trace.add(trace::Action::CreateGeneralRenderPipeline {
4248                id: render_pipeline.to_trace(),
4249                desc: desc.to_trace(),
4250            });
4251        }
4252        (render_pipeline, error)
4253    }
4254
4255    pub fn create_render_pipeline_inner(
4256        self: &Arc<Self>,
4257        desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
4258    ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {
4259        use wgt::TextureFormatFeatureFlags as Tfff;
4260
4261        self.check_is_valid()?;
4262
4263        let mut shader_binding_sizes = FastHashMap::default();
4264
4265        let color_targets = desc
4266            .fragment
4267            .as_ref()
4268            .map_or(&[][..], |fragment| &fragment.targets);
4269        let depth_stencil_state = desc.depth_stencil.as_ref();
4270
4271        check_color_attachment_count(color_targets.len(), self.limits.max_color_attachments)?;
4272
4273        {
4274            let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
4275                color_targets.iter().filter_map(|x| x.as_ref()).collect();
4276            if !cts.is_empty() && {
4277                let first = &cts[0];
4278                cts[1..]
4279                    .iter()
4280                    .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
4281            } {
4282                self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
4283            }
4284        }
4285
4286        let mut io = validation::StageIo::default();
4287        let mut validated_stages = wgt::ShaderStages::empty();
4288
4289        let mut vertex_steps;
4290        let mut hal_vertex_buffer_layouts;
4291        let mut total_attributes;
4292        let mut dual_source_blending = false;
4293        let mut has_depth_attachment = false;
4294        if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
4295            if vertex.buffers.len() > self.limits.max_vertex_buffers as usize {
4296                return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
4297                    given: vertex.buffers.len() as u32,
4298                    limit: self.limits.max_vertex_buffers,
4299                });
4300            }
4301
4302            vertex_steps = Vec::with_capacity(vertex.buffers.len());
4303            hal_vertex_buffer_layouts = Vec::with_capacity(vertex.buffers.len());
4304            total_attributes = 0;
4305            for (i, vb_state) in vertex.buffers.iter().enumerate() {
4306                let Some(vb_state) = vb_state else {
4307                    vertex_steps.push(None);
4308                    hal_vertex_buffer_layouts.push(None);
4309                    continue;
4310                };
4311
4312                // https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpuvertexbufferlayout
4313
4314                if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
4315                    return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
4316                        index: i as u32,
4317                        given: vb_state.array_stride as u32,
4318                        limit: self.limits.max_vertex_buffer_array_stride,
4319                    });
4320                }
4321                if vb_state.array_stride % wgt::VERTEX_ALIGNMENT != 0 {
4322                    return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
4323                        index: i as u32,
4324                        stride: vb_state.array_stride,
4325                    });
4326                }
4327
4328                let max_stride = if vb_state.array_stride == 0 {
4329                    self.limits.max_vertex_buffer_array_stride as u64
4330                } else {
4331                    vb_state.array_stride
4332                };
4333                let mut last_stride = 0;
4334                for attribute in vb_state.attributes.iter() {
4335                    let attribute_stride = attribute.offset + attribute.format.size();
4336                    if attribute_stride > max_stride {
4337                        return Err(
4338                            pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge {
4339                                location: attribute.shader_location,
4340                                given: attribute_stride as u32,
4341                                limit: max_stride as u32,
4342                            },
4343                        );
4344                    }
4345
4346                    let required_offset_alignment = attribute.format.size().min(4);
4347                    if attribute.offset % required_offset_alignment != 0 {
4348                        return Err(
4349                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4350                                location: attribute.shader_location,
4351                                offset: attribute.offset,
4352                            },
4353                        );
4354                    }
4355
4356                    if attribute.shader_location >= self.limits.max_vertex_attributes {
4357                        return Err(
4358                            pipeline::CreateRenderPipelineError::VertexAttributeLocationTooLarge {
4359                                given: attribute.shader_location,
4360                                limit: self.limits.max_vertex_attributes,
4361                            },
4362                        );
4363                    }
4364
4365                    last_stride = last_stride.max(attribute_stride);
4366                }
4367
4368                vertex_steps.push(Some(pipeline::VertexStep {
4369                    stride: vb_state.array_stride,
4370                    last_stride,
4371                    mode: vb_state.step_mode,
4372                }));
4373                hal_vertex_buffer_layouts.push(if vb_state.attributes.is_empty() {
4374                    None
4375                } else {
4376                    Some(hal::VertexBufferLayout {
4377                        array_stride: vb_state.array_stride,
4378                        step_mode: vb_state.step_mode,
4379                        attributes: vb_state.attributes.as_ref(),
4380                    })
4381                });
4382
4383                for attribute in vb_state.attributes.iter() {
4384                    if attribute.offset >= 0x10000000 {
4385                        return Err(
4386                            pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4387                                location: attribute.shader_location,
4388                                offset: attribute.offset,
4389                            },
4390                        );
4391                    }
4392
4393                    if let wgt::VertexFormat::Float64
4394                    | wgt::VertexFormat::Float64x2
4395                    | wgt::VertexFormat::Float64x3
4396                    | wgt::VertexFormat::Float64x4 = attribute.format
4397                    {
4398                        self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
4399                    }
4400
4401                    let previous = io.varyings.insert(
4402                        attribute.shader_location,
4403                        validation::InterfaceVar::vertex_attribute(attribute.format),
4404                    );
4405
4406                    if previous.is_some() {
4407                        return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
4408                            attribute.shader_location,
4409                        ));
4410                    }
4411                }
4412                total_attributes += vb_state.attributes.len();
4413            }
4414
4415            if total_attributes > self.limits.max_vertex_attributes as usize {
4416                return Err(
4417                    pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
4418                        given: total_attributes as u32,
4419                        limit: self.limits.max_vertex_attributes,
4420                    },
4421                );
4422            }
4423        } else {
4424            vertex_steps = Vec::new();
4425            hal_vertex_buffer_layouts = Vec::new();
4426        };
4427
4428        if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
4429            return Err(
4430                pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
4431                    strip_index_format: desc.primitive.strip_index_format,
4432                    topology: desc.primitive.topology,
4433                },
4434            );
4435        }
4436
4437        if desc.primitive.unclipped_depth {
4438            self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
4439        }
4440
4441        if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
4442            self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
4443        }
4444        if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
4445            self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
4446        }
4447
4448        if desc.primitive.conservative {
4449            self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
4450        }
4451
4452        if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
4453            return Err(
4454                pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
4455            );
4456        }
4457
4458        let mut target_specified = false;
4459
4460        for (i, cs) in color_targets.iter().enumerate() {
4461            if let Some(cs) = cs.as_ref() {
4462                target_specified = true;
4463                let error = 'error: {
4464                    // This is expected to be the operative check for illegal write mask
4465                    // values (larger than 15), because WebGPU requires that it be validated
4466                    // on the device timeline.
4467                    if cs.write_mask.contains_unknown_bits() {
4468                        break 'error Some(ColorStateError::InvalidWriteMask(cs.write_mask));
4469                    }
4470
4471                    let format_features = self.describe_format_features(cs.format)?;
4472                    if !format_features
4473                        .allowed_usages
4474                        .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4475                    {
4476                        break 'error Some(ColorStateError::FormatNotRenderable(cs.format));
4477                    }
4478                    if cs.blend.is_some() && !format_features.flags.contains(Tfff::BLENDABLE) {
4479                        break 'error Some(ColorStateError::FormatNotBlendable(cs.format));
4480                    }
4481                    if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
4482                        break 'error Some(ColorStateError::FormatNotColor(cs.format));
4483                    }
4484
4485                    if desc.multisample.count > 1
4486                        && !format_features
4487                            .flags
4488                            .sample_count_supported(desc.multisample.count)
4489                    {
4490                        break 'error Some(ColorStateError::InvalidSampleCount(
4491                            desc.multisample.count,
4492                            cs.format,
4493                            cs.format
4494                                .guaranteed_format_features(self.features)
4495                                .flags
4496                                .supported_sample_counts(),
4497                            self.adapter
4498                                .get_texture_format_features(cs.format)
4499                                .flags
4500                                .supported_sample_counts(),
4501                        ));
4502                    }
4503
4504                    if let Some(blend_mode) = cs.blend {
4505                        for component in [&blend_mode.color, &blend_mode.alpha] {
4506                            for factor in [component.src_factor, component.dst_factor] {
4507                                if factor.ref_second_blend_source() {
4508                                    self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
4509                                    if i == 0 {
4510                                        dual_source_blending = true;
4511                                    } else {
4512                                        break 'error Some(
4513                                            ColorStateError::BlendFactorOnUnsupportedTarget {
4514                                                factor,
4515                                                target: i as u32,
4516                                            },
4517                                        );
4518                                    }
4519                                }
4520
4521                                if [wgt::BlendOperation::Min, wgt::BlendOperation::Max]
4522                                    .contains(&component.operation)
4523                                    && factor != wgt::BlendFactor::One
4524                                {
4525                                    break 'error Some(ColorStateError::InvalidMinMaxBlendFactor {
4526                                        factor,
4527                                        target: i as u32,
4528                                    });
4529                                }
4530                            }
4531                        }
4532                    }
4533
4534                    break 'error None;
4535                };
4536                if let Some(e) = error {
4537                    return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
4538                }
4539            }
4540        }
4541
4542        if dual_source_blending && color_targets.len() > 1 {
4543            return Err(
4544                pipeline::CreateRenderPipelineError::DualSourceBlendingWithMultipleColorTargets {
4545                    count: color_targets.len(),
4546                },
4547            );
4548        }
4549
4550        validation::validate_color_attachment_bytes_per_sample(
4551            color_targets.iter().flatten().map(|cs| cs.format),
4552            self.limits.max_color_attachment_bytes_per_sample,
4553        )
4554        .map_err(pipeline::CreateRenderPipelineError::ColorAttachment)?;
4555
4556        if let Some(ds) = depth_stencil_state {
4557            // See <https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpudepthstencilstate>.
4558            target_specified = true;
4559            let error = 'error: {
4560                if !ds.format.is_depth_stencil_format() {
4561                    // This error case is not redundant with the aspect check below when
4562                    // neither depth nor stencil is enabled at all.
4563                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepthOrStencil(
4564                        ds.format,
4565                    ));
4566                }
4567
4568                let format_features = self.describe_format_features(ds.format)?;
4569                if !format_features
4570                    .allowed_usages
4571                    .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4572                {
4573                    break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(
4574                        ds.format,
4575                    ));
4576                }
4577
4578                let aspect = hal::FormatAspects::from(ds.format);
4579                if aspect.contains(hal::FormatAspects::DEPTH) {
4580                    has_depth_attachment = true;
4581                } else if ds.is_depth_enabled() {
4582                    break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
4583                }
4584                if has_depth_attachment {
4585                    let Some(depth_write_enabled) = ds.depth_write_enabled else {
4586                        break 'error Some(
4587                            pipeline::DepthStencilStateError::MissingDepthWriteEnabled(ds.format),
4588                        );
4589                    };
4590
4591                    let depth_compare_required = depth_write_enabled
4592                        || ds.stencil.front.depth_fail_op != wgt::StencilOperation::Keep
4593                        || ds.stencil.back.depth_fail_op != wgt::StencilOperation::Keep;
4594                    if depth_compare_required && ds.depth_compare.is_none() {
4595                        break 'error Some(pipeline::DepthStencilStateError::MissingDepthCompare(
4596                            ds.format,
4597                        ));
4598                    }
4599                }
4600
4601                if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
4602                    break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(
4603                        ds.format,
4604                    ));
4605                }
4606                if desc.multisample.count > 1
4607                    && !format_features
4608                        .flags
4609                        .sample_count_supported(desc.multisample.count)
4610                {
4611                    break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(
4612                        desc.multisample.count,
4613                        ds.format,
4614                        ds.format
4615                            .guaranteed_format_features(self.features)
4616                            .flags
4617                            .supported_sample_counts(),
4618                        self.adapter
4619                            .get_texture_format_features(ds.format)
4620                            .flags
4621                            .supported_sample_counts(),
4622                    ));
4623                }
4624
4625                break 'error None;
4626            };
4627            if let Some(e) = error {
4628                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
4629            }
4630
4631            if ds.bias.clamp != 0.0 {
4632                self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
4633            }
4634
4635            if (ds.bias.is_enabled() || ds.bias.clamp != 0.0)
4636                && !desc.primitive.topology.is_triangles()
4637            {
4638                return Err(pipeline::CreateRenderPipelineError::DepthStencilState(
4639                    pipeline::DepthStencilStateError::DepthBiasWithIncompatibleTopology(
4640                        desc.primitive.topology,
4641                    ),
4642                ));
4643            }
4644        }
4645
4646        if !target_specified {
4647            return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);
4648        }
4649
4650        let is_auto_layout = desc.layout.is_none();
4651
4652        // Get the pipeline layout from the desc if it is provided.
4653        let pipeline_layout = match desc.layout {
4654            Some(pipeline_layout) => {
4655                pipeline_layout.same_device(self)?;
4656                pipeline_layout.check_valid()?;
4657                Some(pipeline_layout)
4658            }
4659            None => None,
4660        };
4661
4662        let mut binding_layout_source = match pipeline_layout {
4663            Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4664            None => validation::BindingLayoutSource::new_derived(&self.limits),
4665        };
4666
4667        let samples = {
4668            let sc = desc.multisample.count;
4669            if sc == 0 || sc > 32 || !sc.is_power_of_two() {
4670                return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
4671            }
4672            sc
4673        };
4674
4675        let mut vertex_stage = None;
4676        let mut task_stage = None;
4677        let mut mesh_stage = None;
4678        let mut _vertex_entry_point_name = String::new();
4679        let mut _task_entry_point_name = String::new();
4680        let mut _mesh_entry_point_name = String::new();
4681        let mut immediate_slots_required = naga::valid::ImmediateSlots::default();
4682        match desc.vertex {
4683            pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) => {
4684                vertex_stage = {
4685                    let stage_desc = &vertex.stage;
4686                    let stage = validation::ShaderStageForValidation::Vertex {
4687                        topology: desc.primitive.topology,
4688                        compare_function: desc.depth_stencil.as_ref().and_then(|d| d.depth_compare),
4689                    };
4690                    let stage_bit = stage.to_wgt_bit();
4691
4692                    let vertex_shader_module = &stage_desc.module;
4693                    vertex_shader_module.same_device(self)?;
4694
4695                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4696                        stage: stage_bit,
4697                        error,
4698                    };
4699
4700                    _vertex_entry_point_name = vertex_shader_module
4701                        .finalize_entry_point_name(
4702                            stage.to_naga(),
4703                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4704                        )
4705                        .map_err(stage_err)?;
4706
4707                    if let Some(interface) = vertex_shader_module.interface.interface() {
4708                        immediate_slots_required |= interface
4709                            .immediate_slots_required(stage.to_naga(), &_vertex_entry_point_name);
4710                        io = interface
4711                            .check_stage(
4712                                &mut binding_layout_source,
4713                                &mut shader_binding_sizes,
4714                                &_vertex_entry_point_name,
4715                                stage,
4716                                io,
4717                                Some(desc.primitive.topology),
4718                            )
4719                            .map_err(stage_err)?;
4720                        validated_stages |= stage_bit;
4721                    }
4722                    Some(hal::ProgrammableStage {
4723                        module: vertex_shader_module.raw(),
4724                        entry_point: &_vertex_entry_point_name,
4725                        constants: &stage_desc.constants,
4726                        zero_initialize_workgroup_memory: stage_desc
4727                            .zero_initialize_workgroup_memory,
4728                    })
4729                };
4730            }
4731            pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {
4732                self.require_features(wgt::Features::EXPERIMENTAL_MESH_SHADER)?;
4733
4734                task_stage = if let Some(task) = task {
4735                    let stage_desc = &task.stage;
4736                    let stage = validation::ShaderStageForValidation::Task;
4737                    let stage_bit = stage.to_wgt_bit();
4738                    let task_shader_module = &stage_desc.module;
4739                    task_shader_module.same_device(self)?;
4740
4741                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4742                        stage: stage_bit,
4743                        error,
4744                    };
4745
4746                    _task_entry_point_name = task_shader_module
4747                        .finalize_entry_point_name(
4748                            stage.to_naga(),
4749                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4750                        )
4751                        .map_err(stage_err)?;
4752
4753                    if let Some(interface) = task_shader_module.interface.interface() {
4754                        immediate_slots_required |= interface
4755                            .immediate_slots_required(stage.to_naga(), &_task_entry_point_name);
4756                        io = interface
4757                            .check_stage(
4758                                &mut binding_layout_source,
4759                                &mut shader_binding_sizes,
4760                                &_task_entry_point_name,
4761                                stage,
4762                                io,
4763                                Some(desc.primitive.topology),
4764                            )
4765                            .map_err(stage_err)?;
4766                        validated_stages |= stage_bit;
4767                    }
4768                    Some(hal::ProgrammableStage {
4769                        module: task_shader_module.raw(),
4770                        entry_point: &_task_entry_point_name,
4771                        constants: &stage_desc.constants,
4772                        zero_initialize_workgroup_memory: stage_desc
4773                            .zero_initialize_workgroup_memory,
4774                    })
4775                } else {
4776                    None
4777                };
4778                mesh_stage = {
4779                    let stage_desc = &mesh.stage;
4780                    let stage = validation::ShaderStageForValidation::Mesh;
4781                    let stage_bit = stage.to_wgt_bit();
4782                    let mesh_shader_module = &stage_desc.module;
4783                    mesh_shader_module.same_device(self)?;
4784
4785                    let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4786                        stage: stage_bit,
4787                        error,
4788                    };
4789
4790                    _mesh_entry_point_name = mesh_shader_module
4791                        .finalize_entry_point_name(
4792                            stage.to_naga(),
4793                            stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4794                        )
4795                        .map_err(stage_err)?;
4796
4797                    if let Some(interface) = mesh_shader_module.interface.interface() {
4798                        immediate_slots_required |= interface
4799                            .immediate_slots_required(stage.to_naga(), &_mesh_entry_point_name);
4800                        io = interface
4801                            .check_stage(
4802                                &mut binding_layout_source,
4803                                &mut shader_binding_sizes,
4804                                &_mesh_entry_point_name,
4805                                stage,
4806                                io,
4807                                Some(desc.primitive.topology),
4808                            )
4809                            .map_err(stage_err)?;
4810                        validated_stages |= stage_bit;
4811                    }
4812                    Some(hal::ProgrammableStage {
4813                        module: mesh_shader_module.raw(),
4814                        entry_point: &_mesh_entry_point_name,
4815                        constants: &stage_desc.constants,
4816                        zero_initialize_workgroup_memory: stage_desc
4817                            .zero_initialize_workgroup_memory,
4818                    })
4819                };
4820            }
4821        }
4822
4823        let fragment_entry_point_name;
4824        let fragment_stage = match desc.fragment {
4825            Some(ref fragment_state) => {
4826                let stage = validation::ShaderStageForValidation::Fragment {
4827                    dual_source_blending,
4828                    has_depth_attachment,
4829                };
4830                let stage_bit = stage.to_wgt_bit();
4831
4832                let shader_module = &fragment_state.stage.module;
4833                shader_module.same_device(self)?;
4834
4835                let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4836                    stage: stage_bit,
4837                    error,
4838                };
4839
4840                fragment_entry_point_name = shader_module
4841                    .finalize_entry_point_name(
4842                        stage.to_naga(),
4843                        fragment_state
4844                            .stage
4845                            .entry_point
4846                            .as_ref()
4847                            .map(|ep| ep.as_ref()),
4848                    )
4849                    .map_err(stage_err)?;
4850
4851                if let Some(interface) = shader_module.interface.interface() {
4852                    immediate_slots_required |= interface
4853                        .immediate_slots_required(stage.to_naga(), &fragment_entry_point_name);
4854                    io = interface
4855                        .check_stage(
4856                            &mut binding_layout_source,
4857                            &mut shader_binding_sizes,
4858                            &fragment_entry_point_name,
4859                            stage,
4860                            io,
4861                            Some(desc.primitive.topology),
4862                        )
4863                        .map_err(stage_err)?;
4864                    validated_stages |= stage_bit;
4865                }
4866
4867                Some(hal::ProgrammableStage {
4868                    module: shader_module.raw(),
4869                    entry_point: &fragment_entry_point_name,
4870                    constants: &fragment_state.stage.constants,
4871                    zero_initialize_workgroup_memory: fragment_state
4872                        .stage
4873                        .zero_initialize_workgroup_memory,
4874                })
4875            }
4876            None => None,
4877        };
4878
4879        if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
4880            for (i, output) in io.varyings.iter() {
4881                match color_targets.get(*i as usize) {
4882                    Some(Some(state)) => {
4883                        validation::check_texture_format(state.format, &output.ty).map_err(
4884                            |pipeline| {
4885                                pipeline::CreateRenderPipelineError::ColorState(
4886                                    *i as u8,
4887                                    ColorStateError::IncompatibleFormat {
4888                                        pipeline,
4889                                        shader: output.ty,
4890                                    },
4891                                )
4892                            },
4893                        )?;
4894                    }
4895                    _ => {
4896                        log::debug!(
4897                            "The fragment stage {:?} output @location({}) values are ignored",
4898                            fragment_stage
4899                                .as_ref()
4900                                .map_or("", |stage| stage.entry_point),
4901                            i
4902                        );
4903                    }
4904                }
4905            }
4906        }
4907        let last_stage = match desc.fragment {
4908            Some(_) => wgt::ShaderStages::FRAGMENT,
4909            None => wgt::ShaderStages::VERTEX,
4910        };
4911        if is_auto_layout && !validated_stages.contains(last_stage) {
4912            return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
4913        }
4914
4915        let pipeline_layout = match binding_layout_source {
4916            validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
4917            validation::BindingLayoutSource::Derived(entries) => {
4918                let immediate_size = {
4919                    let immediate_size_of = |sm: &pipeline::ShaderModule| {
4920                        sm.interface.interface().map(|i| i.immediate_size)
4921                    };
4922                    let vertex = match desc.vertex {
4923                        pipeline::RenderPipelineVertexProcessor::Vertex(ref v) => {
4924                            immediate_size_of(&v.stage.module)
4925                        }
4926                        pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => task
4927                            .as_ref()
4928                            .and_then(|t| immediate_size_of(&t.stage.module))
4929                            .max(immediate_size_of(&mesh.stage.module)),
4930                    };
4931                    let fragment = desc
4932                        .fragment
4933                        .as_ref()
4934                        .and_then(|f| immediate_size_of(&f.stage.module));
4935                    vertex.max(fragment).unwrap_or(0)
4936                };
4937                self.create_derived_pipeline_layout(entries, immediate_size)?
4938            }
4939        };
4940
4941        if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
4942            let bind_groups_plus_vertex_buffers =
4943                u32::try_from(pipeline_layout.bind_group_layouts.len() + vertex.buffers.len())
4944                    .unwrap();
4945            if bind_groups_plus_vertex_buffers > self.limits.max_bind_groups_plus_vertex_buffers {
4946                return Err(
4947                    pipeline::CreateRenderPipelineError::TooManyBindGroupsPlusVertexBuffers {
4948                        given: bind_groups_plus_vertex_buffers,
4949                        limit: self.limits.max_bind_groups_plus_vertex_buffers,
4950                    },
4951                );
4952            }
4953
4954            let given = pipeline_layout
4955                .buffers_and_acceleration_structures_in_vertex_stage
4956                .saturating_add(vertex.buffers.len() as u32);
4957            if !self
4958                .instance_flags
4959                .contains(wgt::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
4960            {
4961                let limit = self
4962                    .limits
4963                    .max_buffers_and_acceleration_structures_per_shader_stage;
4964                if given > limit {
4965                    return Err(
4966                    pipeline::CreateRenderPipelineError::TooManyBuffersAndAccelerationStructuresInVertexStage {
4967                        given,
4968                        limit,
4969                    },
4970                );
4971                }
4972            }
4973        }
4974
4975        // Multiview is only supported if the feature is enabled
4976        if let Some(mv_mask) = desc.multiview_mask {
4977            self.require_features(wgt::Features::MULTIVIEW)?;
4978            if !(mv_mask.get() + 1).is_power_of_two() {
4979                self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
4980            }
4981        }
4982
4983        if !self
4984            .downlevel
4985            .flags
4986            .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
4987        {
4988            for (binding, size) in shader_binding_sizes.iter() {
4989                if size.get() % 16 != 0 {
4990                    return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
4991                        binding: binding.binding,
4992                        group: binding.group,
4993                        size: size.get(),
4994                    });
4995                }
4996            }
4997        }
4998
4999        let late_sized_buffer_groups =
5000            Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
5001
5002        let cache = match desc.cache {
5003            Some(cache) => {
5004                cache.same_device(self)?;
5005                Some(cache)
5006            }
5007            None => None,
5008        };
5009
5010        let is_mesh = mesh_stage.is_some();
5011        let has_task_shader = task_stage.is_some();
5012        let raw = {
5013            let pipeline_desc = hal::RenderPipelineDescriptor {
5014                label: desc.label.to_hal(self.instance_flags),
5015                layout: pipeline_layout.raw()?,
5016                vertex_processor: match vertex_stage {
5017                    Some(vertex_stage) => hal::VertexProcessor::Standard {
5018                        vertex_buffers: &hal_vertex_buffer_layouts,
5019                        vertex_stage,
5020                    },
5021                    None => hal::VertexProcessor::Mesh {
5022                        task_stage,
5023                        mesh_stage: mesh_stage.unwrap(),
5024                    },
5025                },
5026                primitive: desc.primitive,
5027                depth_stencil: desc.depth_stencil.clone(),
5028                multisample: desc.multisample,
5029                fragment_stage,
5030                color_targets,
5031                multiview_mask: desc.multiview_mask,
5032                cache: cache.as_ref().map(|it| it.raw()),
5033            };
5034            unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
5035                |err| match err {
5036                    hal::PipelineError::Device(error) => {
5037                        pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))
5038                    }
5039                    hal::PipelineError::Linkage(stage, msg) => {
5040                        pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
5041                    }
5042                    hal::PipelineError::EntryPoint(stage) => {
5043                        pipeline::CreateRenderPipelineError::Internal {
5044                            stage: hal::auxil::map_naga_stage(stage),
5045                            error: ENTRYPOINT_FAILURE_ERROR.to_string(),
5046                        }
5047                    }
5048                    hal::PipelineError::PipelineConstants(stage, error) => {
5049                        pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }
5050                    }
5051                },
5052            )?
5053        };
5054
5055        let pass_context = RenderPassContext {
5056            attachments: AttachmentData {
5057                colors: color_targets
5058                    .iter()
5059                    .map(|state| state.as_ref().map(|s| s.format))
5060                    .collect(),
5061                resolves: ArrayVec::new(),
5062                depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
5063            },
5064            sample_count: samples,
5065            multiview_mask: desc.multiview_mask,
5066        };
5067
5068        let mut flags = pipeline::PipelineFlags::empty();
5069        for state in color_targets.iter().filter_map(|s| s.as_ref()) {
5070            if let Some(ref bs) = state.blend {
5071                if bs.color.uses_constant() | bs.alpha.uses_constant() {
5072                    flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
5073                }
5074            }
5075        }
5076        if let Some(ds) = depth_stencil_state.as_ref() {
5077            if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
5078                flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
5079            }
5080            if !ds.is_depth_read_only() {
5081                flags |= pipeline::PipelineFlags::WRITES_DEPTH;
5082            }
5083            if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
5084                flags |= pipeline::PipelineFlags::WRITES_STENCIL;
5085            }
5086        }
5087        let shader_modules = {
5088            let mut shader_modules = ArrayVec::new();
5089            match desc.vertex {
5090                pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
5091                    shader_modules.push(vertex.stage.module)
5092                }
5093                pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
5094                    if let Some(task) = task {
5095                        shader_modules.push(task.stage.module);
5096                    }
5097                    shader_modules.push(mesh.stage.module);
5098                }
5099            }
5100            shader_modules.extend(desc.fragment.map(|f| f.stage.module));
5101            shader_modules
5102        };
5103
5104        let pipeline = pipeline::RenderPipeline {
5105            state: ResourceState::Valid(pipeline::RenderPipelineState {
5106                raw: ManuallyDrop::new(raw),
5107                layout: pipeline_layout.clone(),
5108            }),
5109            device: self.clone(),
5110            pass_context,
5111            _shader_modules: shader_modules,
5112            flags,
5113            topology: desc.primitive.topology,
5114            strip_index_format: desc.primitive.strip_index_format,
5115            vertex_steps,
5116            late_sized_buffer_groups,
5117            immediate_slots_required,
5118            label: desc.label.to_string(),
5119            tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),
5120            is_mesh,
5121            has_task_shader,
5122        };
5123
5124        let pipeline = Arc::new(pipeline);
5125
5126        if is_auto_layout {
5127            for bgl in pipeline_layout.bind_group_layouts.iter() {
5128                let Some(bgl) = bgl else {
5129                    continue;
5130                };
5131
5132                // `bind_group_layouts` might contain duplicate entries, so we need to ignore the
5133                // result.
5134                let _ = bgl.exclusive_pipeline.set((&pipeline).into());
5135            }
5136        }
5137
5138        Ok(pipeline)
5139    }
5140
5141    /// # Safety
5142    /// The `data` field on `desc` must have previously been returned from
5143    /// [`crate::global::Global::pipeline_cache_get_data`]
5144    pub unsafe fn create_pipeline_cache(
5145        self: &Arc<Self>,
5146        desc: &pipeline::PipelineCacheDescriptor,
5147    ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {
5148        use crate::pipeline_cache;
5149
5150        self.check_is_valid()?;
5151
5152        self.require_features(wgt::Features::PIPELINE_CACHE)?;
5153        let data = if let Some((data, validation_key)) = desc
5154            .data
5155            .as_ref()
5156            .zip(self.raw().pipeline_cache_validation_key())
5157        {
5158            let data = pipeline_cache::validate_pipeline_cache(
5159                data,
5160                &self.adapter.raw.info,
5161                validation_key,
5162            );
5163            match data {
5164                Ok(data) => Some(data),
5165                Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),
5166                // If the error was unavoidable and we are asked to fallback, do so
5167                Err(_) => None,
5168            }
5169        } else {
5170            None
5171        };
5172        let cache_desc = hal::PipelineCacheDescriptor {
5173            data,
5174            label: desc.label.to_hal(self.instance_flags),
5175        };
5176        let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {
5177            Ok(raw) => raw,
5178            Err(e) => match e {
5179                hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),
5180            },
5181        };
5182        let cache = pipeline::PipelineCache {
5183            device: self.clone(),
5184            label: desc.label.to_string(),
5185            // This would be none in the error condition, which we don't implement yet
5186            raw: ManuallyDrop::new(raw),
5187        };
5188
5189        let cache = Arc::new(cache);
5190
5191        Ok(cache)
5192    }
5193
5194    fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {
5195        // Variant of adapter.get_texture_format_features that takes device features into account
5196        use wgt::TextureFormatFeatureFlags as tfsc;
5197        let mut format_features = self.adapter.get_texture_format_features(format);
5198        if (format == TextureFormat::R32Float
5199            || format == TextureFormat::Rg32Float
5200            || format == TextureFormat::Rgba32Float)
5201            && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
5202        {
5203            format_features.flags.set(tfsc::FILTERABLE, false);
5204        }
5205        format_features
5206    }
5207
5208    pub(crate) fn describe_format_features(
5209        &self,
5210        format: TextureFormat,
5211    ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
5212        self.require_features(format.required_features())?;
5213
5214        let using_device_features = self
5215            .features
5216            .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
5217        // If we're running downlevel, we need to manually ask the backend what
5218        // we can use as we can't trust WebGPU.
5219        let downlevel = !self
5220            .downlevel
5221            .flags
5222            .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
5223
5224        if using_device_features || downlevel {
5225            Ok(self.get_texture_format_features(format))
5226        } else {
5227            Ok(format.guaranteed_format_features(self.features))
5228        }
5229    }
5230
5231    #[cfg(feature = "replay")]
5232    pub(crate) fn wait_for_submit(
5233        &self,
5234        submission_index: crate::SubmissionIndex,
5235    ) -> Result<(), DeviceError> {
5236        let last_done_index = unsafe { self.raw().get_fence_value(self.fence.as_ref()) }
5237            .map_err(|e| self.handle_hal_error(e))?;
5238        if last_done_index < submission_index {
5239            unsafe { self.raw().wait(self.fence.as_ref(), submission_index, None) }
5240                .map_err(|e| self.handle_hal_error(e))?;
5241            if let Some(queue) = self.get_queue() {
5242                let closures = queue.lock_life().triage_submissions(submission_index);
5243                assert!(
5244                    closures.is_empty(),
5245                    "wait_for_submit is not expected to work with closures"
5246                );
5247            }
5248        }
5249        Ok(())
5250    }
5251
5252    pub fn create_query_set(
5253        self: &Arc<Self>,
5254        desc: &resource::QuerySetDescriptor,
5255    ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {
5256        use resource::CreateQuerySetError as Error;
5257
5258        self.check_is_valid()?;
5259
5260        match desc.ty {
5261            wgt::QueryType::Occlusion => {}
5262            wgt::QueryType::Timestamp => {
5263                self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
5264            }
5265            wgt::QueryType::PipelineStatistics(..) => {
5266                self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
5267            }
5268        }
5269
5270        if desc.count == 0 {
5271            return Err(Error::ZeroCount);
5272        }
5273
5274        if desc.count > wgt::QUERY_SET_MAX_QUERIES {
5275            return Err(Error::TooManyQueries {
5276                count: desc.count,
5277                maximum: wgt::QUERY_SET_MAX_QUERIES,
5278            });
5279        }
5280
5281        let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
5282
5283        let raw = unsafe { self.raw().create_query_set(&hal_desc) }
5284            .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
5285
5286        let query_set = QuerySet {
5287            raw: Snatchable::new(raw),
5288            device: self.clone(),
5289            label: desc.label.to_string(),
5290            tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),
5291            desc: desc.map_label(|_| ()),
5292            initialized_slots: Mutex::new(
5293                rank::QUERY_SET_INITIALIZED_SLOTS,
5294                bit_vec::BitVec::from_elem(desc.count as usize, false),
5295            ),
5296        };
5297
5298        let query_set = Arc::new(query_set);
5299
5300        Ok(query_set)
5301    }
5302
5303    pub fn configure_surface(
5304        self: &Arc<Self>,
5305        surface: &crate::instance::Surface,
5306        config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
5307    ) -> Option<present::ConfigureSurfaceError> {
5308        use present::ConfigureSurfaceError as E;
5309        profiling::scope!("surface_configure");
5310
5311        log::debug!("configuring surface with {config:?}");
5312
5313        let error = 'error: {
5314            // User callbacks must not be called while we are holding locks.
5315            let user_callbacks;
5316            {
5317                if let Err(e) = self.check_is_valid() {
5318                    break 'error e.into();
5319                }
5320
5321                let caps = match surface.get_capabilities(&self.adapter) {
5322                    Ok(caps) => caps,
5323                    Err(_) => break 'error E::UnsupportedQueueFamily,
5324                };
5325
5326                let mut hal_view_formats = Vec::new();
5327                for format in config.view_formats.iter() {
5328                    if *format == config.format {
5329                        continue;
5330                    }
5331                    if !caps.formats.iter().any(|fc| fc.format == config.format) {
5332                        break 'error E::UnsupportedFormat {
5333                            requested: config.format,
5334                            available: caps.texture_formats().collect(),
5335                        };
5336                    }
5337                    if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
5338                        break 'error E::InvalidViewFormat(*format, config.format);
5339                    }
5340                    hal_view_formats.push(*format);
5341                }
5342
5343                if !hal_view_formats.is_empty() {
5344                    if let Err(missing_flag) =
5345                        self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
5346                    {
5347                        break 'error E::MissingDownlevelFlags(missing_flag);
5348                    }
5349                }
5350
5351                let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
5352                    *caps.maximum_frame_latency.start(),
5353                    *caps.maximum_frame_latency.end(),
5354                );
5355                let mut hal_config = hal::SurfaceConfiguration {
5356                    maximum_frame_latency,
5357                    present_mode: config.present_mode,
5358                    composite_alpha_mode: config.alpha_mode,
5359                    format: config.format,
5360                    color_space: config.color_space,
5361                    extent: wgt::Extent3d {
5362                        width: config.width,
5363                        height: config.height,
5364                        depth_or_array_layers: 1,
5365                    },
5366                    usage: conv::map_texture_usage(
5367                        config.usage,
5368                        hal::FormatAspects::COLOR,
5369                        wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
5370                            | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
5371                            | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
5372                    ),
5373                    view_formats: hal_view_formats,
5374                };
5375
5376                if let Err(error) = validate_surface_configuration(
5377                    &mut hal_config,
5378                    &caps,
5379                    self.limits.max_texture_dimension_2d,
5380                ) {
5381                    break 'error error;
5382                }
5383
5384                // Wait for all work to finish before configuring the surface.
5385                let snatch_guard = self.snatchable_lock.read();
5386
5387                let maintain_result;
5388                (user_callbacks, maintain_result) =
5389                    self.maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
5390
5391                match maintain_result {
5392                    // Every submission made before this call has finished. A queue that is
5393                    // not empty means another thread submitted since, which happens when
5394                    // an engine renders on its own thread with this device. That work
5395                    // cannot use this surface's textures: none is acquired, which is
5396                    // checked below, and a presented one cannot be submitted. Upstream
5397                    // wgpu accepts both results too.
5398                    Ok(wgt::PollStatus::QueueEmpty | wgt::PollStatus::WaitSucceeded) => {}
5399                    Ok(wgt::PollStatus::Poll) => {
5400                        unreachable!("Cannot get a Poll result from a Wait action.")
5401                    }
5402                    Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
5403                        // On wasm, you cannot actually successfully wait for the surface.
5404                        // However WebGL does not actually require you do this, so ignoring
5405                        // the failure is totally fine. See
5406                        // https://github.com/gfx-rs/wgpu/issues/7363
5407                    }
5408                    Err(e) => {
5409                        break 'error e.into();
5410                    }
5411                }
5412
5413                // All textures must be destroyed before the surface can be re-configured.
5414                if let Some(present) = surface.presentation.lock().take() {
5415                    if present.acquired_texture.is_some() {
5416                        break 'error E::PreviousOutputExists;
5417                    }
5418                }
5419
5420                // TODO: Texture views may still be alive that point to the texture.
5421                // this will allow the user to render to the surface texture, long after
5422                // it has been removed.
5423                //
5424                // https://github.com/gfx-rs/wgpu/issues/4105
5425
5426                let surface_raw = surface.raw(self.backend()).unwrap();
5427                match unsafe { surface_raw.configure(self.raw(), &hal_config) } {
5428                    Ok(()) => (),
5429                    Err(error) => {
5430                        break 'error match error {
5431                            hal::SurfaceError::Outdated
5432                            | hal::SurfaceError::Lost
5433                            | hal::SurfaceError::Occluded
5434                            | hal::SurfaceError::Timeout => E::InvalidSurface,
5435                            hal::SurfaceError::Device(error) => {
5436                                E::Device(self.handle_hal_error(error))
5437                            }
5438                            hal::SurfaceError::Other(message) => {
5439                                log::error!("surface configuration failed: {message}");
5440                                E::InvalidSurface
5441                            }
5442                        }
5443                    }
5444                }
5445
5446                let mut presentation = surface.presentation.lock();
5447                *presentation = Some(present::Presentation {
5448                    device: Arc::clone(self),
5449                    config: config.clone(),
5450                    acquired_texture: None,
5451                });
5452            }
5453
5454            user_callbacks.fire();
5455            return None;
5456        };
5457
5458        Some(error)
5459    }
5460
5461    fn lose(&self, message: &str) {
5462        // Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device.
5463
5464        // Mark the device explicitly as invalid. This is checked in various
5465        // places to prevent new work from being submitted.
5466        self.valid.store(false, Ordering::Release);
5467
5468        // 1) Resolve the GPUDevice device.lost promise.
5469        if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
5470            device_lost_closure(DeviceLostReason::Unknown, message.to_string());
5471        }
5472
5473        // 2) Complete any outstanding mapAsync() steps.
5474        // 3) Complete any outstanding onSubmittedWorkDone() steps.
5475
5476        // These parts are passively accomplished by setting valid to false,
5477        // since that will prevent any new work from being added to the queues.
5478        // Future calls to poll_devices will continue to check the work queues
5479        // until they are cleared, and then drop the device.
5480    }
5481
5482    fn release_gpu_resources(&self) {
5483        // This is called when the device is lost, which makes every associated
5484        // resource invalid and unusable. This is an opportunity to release all of
5485        // the underlying gpu resources, even though the objects remain visible to
5486        // the user agent. We purge this memory naturally when resources have been
5487        // moved into the appropriate buckets, so this function just needs to
5488        // initiate movement into those buckets, and it can do that by calling
5489        // "destroy" on all the resources we know about.
5490
5491        // During these iterations, we discard all errors. We don't care!
5492        let trackers = self.trackers.lock();
5493        for buffer in trackers.buffers.used_resources() {
5494            if let Some(buffer) = Weak::upgrade(buffer) {
5495                buffer.destroy();
5496            }
5497        }
5498        for texture in trackers.textures.used_resources() {
5499            if let Some(texture) = Weak::upgrade(texture) {
5500                texture.destroy();
5501            }
5502        }
5503    }
5504
5505    pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {
5506        UsageScope::new_pooled(
5507            &self.usage_scopes,
5508            &self.tracker_indices,
5509            self.ordered_buffer_usages,
5510            self.ordered_texture_usages,
5511        )
5512    }
5513
5514    pub fn get_hal_counters(&self) -> wgt::HalCounters {
5515        self.raw().get_internal_counters()
5516    }
5517
5518    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
5519        self.raw().generate_allocator_report()
5520    }
5521}
5522
5523crate::impl_resource_type!(Device);
5524crate::impl_labeled!(Device);
5525crate::impl_storage_item!(Device);