wgpu_core/
binding_model.rs

1use alloc::{
2    borrow::{Cow, ToOwned},
3    boxed::Box,
4    string::String,
5    sync::{Arc, Weak},
6    vec::Vec,
7};
8use core::{fmt, mem::ManuallyDrop, ops::Range};
9
10use arrayvec::ArrayVec;
11use thiserror::Error;
12
13#[cfg(feature = "serde")]
14use serde::Deserialize;
15#[cfg(feature = "serde")]
16use serde::Serialize;
17
18use wgt::error::{ErrorType, WebGpuError};
19
20use crate::{
21    device::{bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures},
22    id::{BindGroupLayoutId, BufferId, ExternalTextureId, SamplerId, TextureViewId, TlasId},
23    init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
24    pipeline::{ComputePipeline, RenderPipeline},
25    resource::{
26        Buffer, DestroyedResourceError, ExternalTexture, InvalidResourceError, Labeled,
27        MissingBufferUsageError, MissingTextureUsageError, RawResourceAccess, ResourceErrorIdent,
28        Sampler, TextureView, Tlas, TrackingData,
29    },
30    resource_log,
31    snatch::{SnatchGuard, Snatchable},
32    track::{BindGroupStates, ResourceUsageCompatibilityError},
33    Label,
34};
35
36#[derive(Clone, Debug, Error)]
37#[non_exhaustive]
38pub enum BindGroupLayoutEntryError {
39    #[error("Cube dimension is not expected for texture storage")]
40    StorageTextureCube,
41    #[error("Atomic storage textures are not allowed by baseline webgpu, they require the native only feature TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES")]
42    StorageTextureAtomic,
43    #[error("Arrays of bindings unsupported for this type of binding")]
44    ArrayUnsupported,
45    #[error("Multisampled binding with sample type `TextureSampleType::Float` must have filterable set to false.")]
46    SampleTypeFloatFilterableBindingMultisampled,
47    #[error("Multisampled texture binding view dimension must be 2d, got {0:?}")]
48    Non2DMultisampled(wgt::TextureViewDimension),
49    #[error(transparent)]
50    MissingFeatures(#[from] MissingFeatures),
51    #[error(transparent)]
52    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
53}
54
55#[derive(Clone, Debug, Error)]
56#[non_exhaustive]
57pub enum CreateBindGroupLayoutError {
58    #[error(transparent)]
59    Device(#[from] DeviceError),
60    #[error("Conflicting binding at index {0}")]
61    ConflictBinding(u32),
62    #[error("Binding {binding} entry is invalid")]
63    Entry {
64        binding: u32,
65        #[source]
66        error: BindGroupLayoutEntryError,
67    },
68    #[error(transparent)]
69    TooManyBindings(BindingTypeMaxCountError),
70    #[error("Bind groups may not contain both a binding array and a dynamically offset buffer")]
71    ContainsBothBindingArrayAndDynamicOffsetArray,
72    #[error("Bind groups may not contain both a binding array and a uniform buffer")]
73    ContainsBothBindingArrayAndUniformBuffer,
74    #[error("Binding index {binding} is greater than the maximum number {maximum}")]
75    InvalidBindingIndex { binding: u32, maximum: u32 },
76    #[error("Invalid visibility {0:?}")]
77    InvalidVisibility(wgt::ShaderStages),
78    #[error("Binding index {binding}: {access:?} access to storage textures with format {format:?} is not supported")]
79    UnsupportedStorageTextureAccess {
80        binding: u32,
81        access: wgt::StorageTextureAccess,
82        format: wgt::TextureFormat,
83    },
84}
85
86impl WebGpuError for CreateBindGroupLayoutError {
87    fn webgpu_error_type(&self) -> ErrorType {
88        match self {
89            Self::Device(e) => e.webgpu_error_type(),
90
91            Self::ConflictBinding(_)
92            | Self::Entry { .. }
93            | Self::TooManyBindings(_)
94            | Self::InvalidBindingIndex { .. }
95            | Self::InvalidVisibility(_)
96            | Self::ContainsBothBindingArrayAndDynamicOffsetArray
97            | Self::ContainsBothBindingArrayAndUniformBuffer
98            | Self::UnsupportedStorageTextureAccess { .. } => ErrorType::Validation,
99        }
100    }
101}
102
103#[derive(Clone, Debug, Error)]
104#[non_exhaustive]
105pub enum BindingError {
106    #[error(transparent)]
107    DestroyedResource(#[from] DestroyedResourceError),
108    #[error("Buffer {buffer}: Binding with size {binding_size} at offset {offset} would overflow buffer size of {buffer_size}")]
109    BindingRangeTooLarge {
110        buffer: ResourceErrorIdent,
111        offset: wgt::BufferAddress,
112        binding_size: u64,
113        buffer_size: u64,
114    },
115    #[error("Buffer {buffer}: Binding offset {offset} is greater than buffer size {buffer_size}")]
116    BindingOffsetTooLarge {
117        buffer: ResourceErrorIdent,
118        offset: wgt::BufferAddress,
119        buffer_size: u64,
120    },
121}
122
123impl WebGpuError for BindingError {
124    fn webgpu_error_type(&self) -> ErrorType {
125        match self {
126            Self::DestroyedResource(e) => e.webgpu_error_type(),
127            Self::BindingRangeTooLarge { .. } | Self::BindingOffsetTooLarge { .. } => {
128                ErrorType::Validation
129            }
130        }
131    }
132}
133
134// TODO: there may be additional variants here that can be extracted into
135// `BindingError`.
136#[derive(Clone, Debug, Error)]
137#[non_exhaustive]
138pub enum CreateBindGroupError {
139    #[error(transparent)]
140    Device(#[from] DeviceError),
141    #[error(transparent)]
142    DestroyedResource(#[from] DestroyedResourceError),
143    #[error(transparent)]
144    BindingError(#[from] BindingError),
145    #[error(
146        "Binding count declared with at most {expected} items, but {actual} items were provided"
147    )]
148    BindingArrayPartialLengthMismatch { actual: usize, expected: usize },
149    #[error(
150        "Binding count declared with exactly {expected} items, but {actual} items were provided"
151    )]
152    BindingArrayLengthMismatch { actual: usize, expected: usize },
153    #[error("Array binding provided zero elements")]
154    BindingArrayZeroLength,
155    #[error("Binding size {actual} of {buffer} is less than minimum {min}")]
156    BindingSizeTooSmall {
157        buffer: ResourceErrorIdent,
158        actual: u64,
159        min: u64,
160    },
161    #[error("{0} binding size is zero")]
162    BindingZeroSize(ResourceErrorIdent),
163    #[error("Number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
164    BindingsNumMismatch { actual: usize, expected: usize },
165    #[error("Binding {0} is used at least twice in the descriptor")]
166    DuplicateBinding(u32),
167    #[error("Unable to find a corresponding declaration for the given binding {0}")]
168    MissingBindingDeclaration(u32),
169    #[error(transparent)]
170    MissingBufferUsage(#[from] MissingBufferUsageError),
171    #[error(transparent)]
172    MissingTextureUsage(#[from] MissingTextureUsageError),
173    #[error("Binding declared as a single item, but bind group is using it as an array")]
174    SingleBindingExpected,
175    #[error("Effective buffer binding size {size} for storage buffers is expected to align to {alignment}, but size is {size}")]
176    UnalignedEffectiveBufferBindingSizeForStorage { alignment: u32, size: u64 },
177    #[error("Buffer offset {0} does not respect device's requested `{1}` limit {2}")]
178    UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32),
179    #[error(
180        "Buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
181    )]
182    BufferRangeTooLarge {
183        binding: u32,
184        given: u32,
185        limit: u32,
186    },
187    #[error("Binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
188    WrongBindingType {
189        // Index of the binding
190        binding: u32,
191        // The type given to the function
192        actual: wgt::BindingType,
193        // Human-readable description of expected types
194        expected: &'static str,
195    },
196    #[error("Texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}")]
197    InvalidTextureMultisample {
198        binding: u32,
199        layout_multisampled: bool,
200        view_samples: u32,
201    },
202    #[error(
203        "Texture binding {} expects sample type {:?}, but was given a view with format {:?} (sample type {:?})",
204        binding,
205        layout_sample_type,
206        view_format,
207        view_sample_type
208    )]
209    InvalidTextureSampleType {
210        binding: u32,
211        layout_sample_type: wgt::TextureSampleType,
212        view_format: wgt::TextureFormat,
213        view_sample_type: wgt::TextureSampleType,
214    },
215    #[error("Texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}")]
216    InvalidTextureDimension {
217        binding: u32,
218        layout_dimension: wgt::TextureViewDimension,
219        view_dimension: wgt::TextureViewDimension,
220    },
221    #[error("Storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}")]
222    InvalidStorageTextureFormat {
223        binding: u32,
224        layout_format: wgt::TextureFormat,
225        view_format: wgt::TextureFormat,
226    },
227    #[error("Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
228    InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },
229    #[error("External texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
230    InvalidExternalTextureMipLevelCount { binding: u32, mip_level_count: u32 },
231    #[error("External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = {format:?} at binding {binding}")]
232    InvalidExternalTextureFormat {
233        binding: u32,
234        format: wgt::TextureFormat,
235    },
236    #[error("Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}")]
237    WrongSamplerComparison {
238        binding: u32,
239        layout_cmp: bool,
240        sampler_cmp: bool,
241    },
242    #[error("Sampler binding {binding} expects filtering = {layout_flt}, but given a sampler with filtering = {sampler_flt}")]
243    WrongSamplerFiltering {
244        binding: u32,
245        layout_flt: bool,
246        sampler_flt: bool,
247    },
248    #[error("TLAS binding {binding} is required to support vertex returns but is missing flag AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN")]
249    MissingTLASVertexReturn { binding: u32 },
250    #[error("Bound texture views can not have both depth and stencil aspects enabled")]
251    DepthStencilAspect,
252    #[error(transparent)]
253    ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
254    #[error(transparent)]
255    InvalidResource(#[from] InvalidResourceError),
256}
257
258impl WebGpuError for CreateBindGroupError {
259    fn webgpu_error_type(&self) -> ErrorType {
260        let e: &dyn WebGpuError = match self {
261            Self::Device(e) => e,
262            Self::DestroyedResource(e) => e,
263            Self::BindingError(e) => e,
264            Self::MissingBufferUsage(e) => e,
265            Self::MissingTextureUsage(e) => e,
266            Self::ResourceUsageCompatibility(e) => e,
267            Self::InvalidResource(e) => e,
268            Self::BindingArrayPartialLengthMismatch { .. }
269            | Self::BindingArrayLengthMismatch { .. }
270            | Self::BindingArrayZeroLength
271            | Self::BindingSizeTooSmall { .. }
272            | Self::BindingsNumMismatch { .. }
273            | Self::BindingZeroSize(_)
274            | Self::DuplicateBinding(_)
275            | Self::MissingBindingDeclaration(_)
276            | Self::SingleBindingExpected
277            | Self::UnalignedEffectiveBufferBindingSizeForStorage { .. }
278            | Self::UnalignedBufferOffset(_, _, _)
279            | Self::BufferRangeTooLarge { .. }
280            | Self::WrongBindingType { .. }
281            | Self::InvalidTextureMultisample { .. }
282            | Self::InvalidTextureSampleType { .. }
283            | Self::InvalidTextureDimension { .. }
284            | Self::InvalidStorageTextureFormat { .. }
285            | Self::InvalidStorageTextureMipLevelCount { .. }
286            | Self::WrongSamplerComparison { .. }
287            | Self::WrongSamplerFiltering { .. }
288            | Self::DepthStencilAspect
289            | Self::MissingTLASVertexReturn { .. }
290            | Self::InvalidExternalTextureMipLevelCount { .. }
291            | Self::InvalidExternalTextureFormat { .. } => return ErrorType::Validation,
292        };
293        e.webgpu_error_type()
294    }
295}
296
297#[derive(Clone, Debug, Error)]
298pub enum BindingZone {
299    #[error("Stage {0:?}")]
300    Stage(wgt::ShaderStages),
301    #[error("Whole pipeline")]
302    Pipeline,
303}
304
305#[derive(Clone, Debug, Error)]
306#[error("Too many bindings of type {kind:?} in {zone}, limit is {limit}, count was {count}. Check the limit `{}` passed to `Adapter::request_device`", .kind.to_config_str())]
307pub struct BindingTypeMaxCountError {
308    pub kind: BindingTypeMaxCountErrorKind,
309    pub zone: BindingZone,
310    pub limit: u32,
311    pub count: u32,
312}
313
314impl WebGpuError for BindingTypeMaxCountError {
315    fn webgpu_error_type(&self) -> ErrorType {
316        ErrorType::Validation
317    }
318}
319
320#[derive(Clone, Debug)]
321pub enum BindingTypeMaxCountErrorKind {
322    DynamicUniformBuffers,
323    DynamicStorageBuffers,
324    SampledTextures,
325    Samplers,
326    StorageBuffers,
327    StorageTextures,
328    UniformBuffers,
329    BindingArrayElements,
330    BindingArraySamplerElements,
331    AccelerationStructures,
332}
333
334impl BindingTypeMaxCountErrorKind {
335    fn to_config_str(&self) -> &'static str {
336        match self {
337            BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {
338                "max_dynamic_uniform_buffers_per_pipeline_layout"
339            }
340            BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {
341                "max_dynamic_storage_buffers_per_pipeline_layout"
342            }
343            BindingTypeMaxCountErrorKind::SampledTextures => {
344                "max_sampled_textures_per_shader_stage"
345            }
346            BindingTypeMaxCountErrorKind::Samplers => "max_samplers_per_shader_stage",
347            BindingTypeMaxCountErrorKind::StorageBuffers => "max_storage_buffers_per_shader_stage",
348            BindingTypeMaxCountErrorKind::StorageTextures => {
349                "max_storage_textures_per_shader_stage"
350            }
351            BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
352            BindingTypeMaxCountErrorKind::BindingArrayElements => {
353                "max_binding_array_elements_per_shader_stage"
354            }
355            BindingTypeMaxCountErrorKind::BindingArraySamplerElements => {
356                "max_binding_array_sampler_elements_per_shader_stage"
357            }
358            BindingTypeMaxCountErrorKind::AccelerationStructures => {
359                "max_acceleration_structures_per_shader_stage"
360            }
361        }
362    }
363}
364
365#[derive(Debug, Default)]
366pub(crate) struct PerStageBindingTypeCounter {
367    vertex: u32,
368    fragment: u32,
369    compute: u32,
370}
371
372impl PerStageBindingTypeCounter {
373    pub(crate) fn add(&mut self, stage: wgt::ShaderStages, count: u32) {
374        if stage.contains(wgt::ShaderStages::VERTEX) {
375            self.vertex += count;
376        }
377        if stage.contains(wgt::ShaderStages::FRAGMENT) {
378            self.fragment += count;
379        }
380        if stage.contains(wgt::ShaderStages::COMPUTE) {
381            self.compute += count;
382        }
383    }
384
385    pub(crate) fn max(&self) -> (BindingZone, u32) {
386        let max_value = self.vertex.max(self.fragment.max(self.compute));
387        let mut stage = wgt::ShaderStages::NONE;
388        if max_value == self.vertex {
389            stage |= wgt::ShaderStages::VERTEX
390        }
391        if max_value == self.fragment {
392            stage |= wgt::ShaderStages::FRAGMENT
393        }
394        if max_value == self.compute {
395            stage |= wgt::ShaderStages::COMPUTE
396        }
397        (BindingZone::Stage(stage), max_value)
398    }
399
400    pub(crate) fn merge(&mut self, other: &Self) {
401        self.vertex += other.vertex;
402        self.fragment += other.fragment;
403        self.compute += other.compute;
404    }
405
406    pub(crate) fn validate(
407        &self,
408        limit: u32,
409        kind: BindingTypeMaxCountErrorKind,
410    ) -> Result<(), BindingTypeMaxCountError> {
411        let (zone, count) = self.max();
412        if limit < count {
413            Err(BindingTypeMaxCountError {
414                kind,
415                zone,
416                limit,
417                count,
418            })
419        } else {
420            Ok(())
421        }
422    }
423}
424
425#[derive(Debug, Default)]
426pub(crate) struct BindingTypeMaxCountValidator {
427    dynamic_uniform_buffers: u32,
428    dynamic_storage_buffers: u32,
429    sampled_textures: PerStageBindingTypeCounter,
430    samplers: PerStageBindingTypeCounter,
431    storage_buffers: PerStageBindingTypeCounter,
432    storage_textures: PerStageBindingTypeCounter,
433    uniform_buffers: PerStageBindingTypeCounter,
434    acceleration_structures: PerStageBindingTypeCounter,
435    binding_array_elements: PerStageBindingTypeCounter,
436    binding_array_sampler_elements: PerStageBindingTypeCounter,
437    has_bindless_array: bool,
438}
439
440impl BindingTypeMaxCountValidator {
441    pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {
442        let count = binding.count.map_or(1, |count| count.get());
443
444        if binding.count.is_some() {
445            self.binding_array_elements.add(binding.visibility, count);
446            self.has_bindless_array = true;
447
448            if let wgt::BindingType::Sampler(_) = binding.ty {
449                self.binding_array_sampler_elements
450                    .add(binding.visibility, count);
451            }
452        } else {
453            match binding.ty {
454                wgt::BindingType::Buffer {
455                    ty: wgt::BufferBindingType::Uniform,
456                    has_dynamic_offset,
457                    ..
458                } => {
459                    self.uniform_buffers.add(binding.visibility, count);
460                    if has_dynamic_offset {
461                        self.dynamic_uniform_buffers += count;
462                    }
463                }
464                wgt::BindingType::Buffer {
465                    ty: wgt::BufferBindingType::Storage { .. },
466                    has_dynamic_offset,
467                    ..
468                } => {
469                    self.storage_buffers.add(binding.visibility, count);
470                    if has_dynamic_offset {
471                        self.dynamic_storage_buffers += count;
472                    }
473                }
474                wgt::BindingType::Sampler { .. } => {
475                    self.samplers.add(binding.visibility, count);
476                }
477                wgt::BindingType::Texture { .. } => {
478                    self.sampled_textures.add(binding.visibility, count);
479                }
480                wgt::BindingType::StorageTexture { .. } => {
481                    self.storage_textures.add(binding.visibility, count);
482                }
483                wgt::BindingType::AccelerationStructure { .. } => {
484                    self.acceleration_structures.add(binding.visibility, count);
485                }
486                wgt::BindingType::ExternalTexture => {
487                    // https://www.w3.org/TR/webgpu/#gpuexternaltexture
488                    // In order to account for many possible representations,
489                    // the binding conservatively uses the following, for each
490                    // external texture:
491                    // * Three sampled textures for up to 3 planes
492                    // * One additional sampled texture for a 3D LUT
493                    // * One sampler to sample the LUT
494                    // * One uniform buffer for metadata
495                    self.sampled_textures.add(binding.visibility, count * 4);
496                    self.samplers.add(binding.visibility, count);
497                    self.uniform_buffers.add(binding.visibility, count);
498                }
499            }
500        }
501    }
502
503    pub(crate) fn merge(&mut self, other: &Self) {
504        self.dynamic_uniform_buffers += other.dynamic_uniform_buffers;
505        self.dynamic_storage_buffers += other.dynamic_storage_buffers;
506        self.sampled_textures.merge(&other.sampled_textures);
507        self.samplers.merge(&other.samplers);
508        self.storage_buffers.merge(&other.storage_buffers);
509        self.storage_textures.merge(&other.storage_textures);
510        self.uniform_buffers.merge(&other.uniform_buffers);
511        self.acceleration_structures
512            .merge(&other.acceleration_structures);
513        self.binding_array_elements
514            .merge(&other.binding_array_elements);
515        self.binding_array_sampler_elements
516            .merge(&other.binding_array_sampler_elements);
517    }
518
519    pub(crate) fn validate(&self, limits: &wgt::Limits) -> Result<(), BindingTypeMaxCountError> {
520        if limits.max_dynamic_uniform_buffers_per_pipeline_layout < self.dynamic_uniform_buffers {
521            return Err(BindingTypeMaxCountError {
522                kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,
523                zone: BindingZone::Pipeline,
524                limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,
525                count: self.dynamic_uniform_buffers,
526            });
527        }
528        if limits.max_dynamic_storage_buffers_per_pipeline_layout < self.dynamic_storage_buffers {
529            return Err(BindingTypeMaxCountError {
530                kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,
531                zone: BindingZone::Pipeline,
532                limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,
533                count: self.dynamic_storage_buffers,
534            });
535        }
536        self.sampled_textures.validate(
537            limits.max_sampled_textures_per_shader_stage,
538            BindingTypeMaxCountErrorKind::SampledTextures,
539        )?;
540        self.samplers.validate(
541            limits.max_samplers_per_shader_stage,
542            BindingTypeMaxCountErrorKind::Samplers,
543        )?;
544        self.storage_buffers.validate(
545            limits.max_storage_buffers_per_shader_stage,
546            BindingTypeMaxCountErrorKind::StorageBuffers,
547        )?;
548        self.storage_textures.validate(
549            limits.max_storage_textures_per_shader_stage,
550            BindingTypeMaxCountErrorKind::StorageTextures,
551        )?;
552        self.uniform_buffers.validate(
553            limits.max_uniform_buffers_per_shader_stage,
554            BindingTypeMaxCountErrorKind::UniformBuffers,
555        )?;
556        self.binding_array_elements.validate(
557            limits.max_binding_array_elements_per_shader_stage,
558            BindingTypeMaxCountErrorKind::BindingArrayElements,
559        )?;
560        self.binding_array_sampler_elements.validate(
561            limits.max_binding_array_sampler_elements_per_shader_stage,
562            BindingTypeMaxCountErrorKind::BindingArraySamplerElements,
563        )?;
564        self.acceleration_structures.validate(
565            limits.max_acceleration_structures_per_shader_stage,
566            BindingTypeMaxCountErrorKind::AccelerationStructures,
567        )?;
568        Ok(())
569    }
570
571    /// Validate that the bind group layout does not contain both a binding array and a dynamic offset array.
572    ///
573    /// This allows us to use `UPDATE_AFTER_BIND` on vulkan for bindless arrays. Vulkan does not allow
574    /// `UPDATE_AFTER_BIND` on dynamic offset arrays. See <https://github.com/gfx-rs/wgpu/issues/6737>
575    pub(crate) fn validate_binding_arrays(&self) -> Result<(), CreateBindGroupLayoutError> {
576        let has_dynamic_offset_array =
577            self.dynamic_uniform_buffers > 0 || self.dynamic_storage_buffers > 0;
578        let has_uniform_buffer = self.uniform_buffers.max().1 > 0;
579        if self.has_bindless_array && has_dynamic_offset_array {
580            return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndDynamicOffsetArray);
581        }
582        if self.has_bindless_array && has_uniform_buffer {
583            return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndUniformBuffer);
584        }
585        Ok(())
586    }
587}
588
589/// Bindable resource and the slot to bind it to.
590/// cbindgen:ignore
591#[derive(Clone, Debug)]
592#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
593pub struct BindGroupEntry<
594    'a,
595    B = BufferId,
596    S = SamplerId,
597    TV = TextureViewId,
598    TLAS = TlasId,
599    ET = ExternalTextureId,
600> where
601    [BufferBinding<B>]: ToOwned,
602    [S]: ToOwned,
603    [TV]: ToOwned,
604    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
605    <[S] as ToOwned>::Owned: fmt::Debug,
606    <[TV] as ToOwned>::Owned: fmt::Debug,
607{
608    /// Slot for which binding provides resource. Corresponds to an entry of the same
609    /// binding index in the [`BindGroupLayoutDescriptor`].
610    pub binding: u32,
611    #[cfg_attr(
612        feature = "serde",
613        serde(bound(deserialize = "BindingResource<'a, B, S, TV, TLAS, ET>: Deserialize<'de>"))
614    )]
615    /// Resource to attach to the binding
616    pub resource: BindingResource<'a, B, S, TV, TLAS, ET>,
617}
618
619/// cbindgen:ignore
620pub type ResolvedBindGroupEntry<'a> = BindGroupEntry<
621    'a,
622    Arc<Buffer>,
623    Arc<Sampler>,
624    Arc<TextureView>,
625    Arc<Tlas>,
626    Arc<ExternalTexture>,
627>;
628
629/// Describes a group of bindings and the resources to be bound.
630#[derive(Clone, Debug)]
631#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
632pub struct BindGroupDescriptor<
633    'a,
634    BGL = BindGroupLayoutId,
635    B = BufferId,
636    S = SamplerId,
637    TV = TextureViewId,
638    TLAS = TlasId,
639    ET = ExternalTextureId,
640> where
641    [BufferBinding<B>]: ToOwned,
642    [S]: ToOwned,
643    [TV]: ToOwned,
644    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
645    <[S] as ToOwned>::Owned: fmt::Debug,
646    <[TV] as ToOwned>::Owned: fmt::Debug,
647    [BindGroupEntry<'a, B, S, TV, TLAS, ET>]: ToOwned,
648    <[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: fmt::Debug,
649{
650    /// Debug label of the bind group.
651    ///
652    /// This will show up in graphics debuggers for easy identification.
653    pub label: Label<'a>,
654    /// The [`BindGroupLayout`] that corresponds to this bind group.
655    pub layout: BGL,
656    #[cfg_attr(
657        feature = "serde",
658        serde(bound(
659            deserialize = "<[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: Deserialize<'de>"
660        ))
661    )]
662    /// The resources to bind to this bind group.
663    #[allow(clippy::type_complexity)]
664    pub entries: Cow<'a, [BindGroupEntry<'a, B, S, TV, TLAS, ET>]>,
665}
666
667/// cbindgen:ignore
668pub type ResolvedBindGroupDescriptor<'a> = BindGroupDescriptor<
669    'a,
670    Arc<BindGroupLayout>,
671    Arc<Buffer>,
672    Arc<Sampler>,
673    Arc<TextureView>,
674    Arc<Tlas>,
675    Arc<ExternalTexture>,
676>;
677
678/// Describes a [`BindGroupLayout`].
679#[derive(Clone, Debug)]
680#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
681pub struct BindGroupLayoutDescriptor<'a> {
682    /// Debug label of the bind group layout.
683    ///
684    /// This will show up in graphics debuggers for easy identification.
685    pub label: Label<'a>,
686    /// Array of entries in this BindGroupLayout
687    pub entries: Cow<'a, [wgt::BindGroupLayoutEntry]>,
688}
689
690/// Used by [`BindGroupLayout`]. It indicates whether the BGL must be
691/// used with a specific pipeline. This constraint only happens when
692/// the BGLs have been derived from a pipeline without a layout.
693#[derive(Clone, Debug)]
694pub(crate) enum ExclusivePipeline {
695    None,
696    Render(Weak<RenderPipeline>),
697    Compute(Weak<ComputePipeline>),
698}
699
700impl fmt::Display for ExclusivePipeline {
701    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
702        match self {
703            ExclusivePipeline::None => f.write_str("None"),
704            ExclusivePipeline::Render(p) => {
705                if let Some(p) = p.upgrade() {
706                    p.error_ident().fmt(f)
707                } else {
708                    f.write_str("RenderPipeline")
709                }
710            }
711            ExclusivePipeline::Compute(p) => {
712                if let Some(p) = p.upgrade() {
713                    p.error_ident().fmt(f)
714                } else {
715                    f.write_str("ComputePipeline")
716                }
717            }
718        }
719    }
720}
721
722#[derive(Debug)]
723pub enum RawBindGroupLayout {
724    Owning(ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>),
725    /// The empty BGL was created by the device and will be destroyed by the device.
726    RefDeviceEmptyBGL,
727}
728
729/// Bind group layout.
730#[derive(Debug)]
731pub struct BindGroupLayout {
732    pub(crate) raw: RawBindGroupLayout,
733    pub(crate) device: Arc<Device>,
734    pub(crate) entries: bgl::EntryMap,
735    /// It is very important that we know if the bind group comes from the BGL pool.
736    ///
737    /// If it does, then we need to remove it from the pool when we drop it.
738    ///
739    /// We cannot unconditionally remove from the pool, as BGLs that don't come from the pool
740    /// (derived BGLs) must not be removed.
741    pub(crate) origin: bgl::Origin,
742    pub(crate) exclusive_pipeline: crate::OnceCellOrLock<ExclusivePipeline>,
743    pub(crate) binding_count_validator: BindingTypeMaxCountValidator,
744    /// The `label` from the descriptor used to create the resource.
745    pub(crate) label: String,
746}
747
748impl Drop for BindGroupLayout {
749    fn drop(&mut self) {
750        resource_log!("Destroy raw {}", self.error_ident());
751        if matches!(self.origin, bgl::Origin::Pool) {
752            self.device.bgl_pool.remove(&self.entries);
753        }
754        match self.raw {
755            RawBindGroupLayout::Owning(ref mut raw) => {
756                // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
757                let raw = unsafe { ManuallyDrop::take(raw) };
758                unsafe {
759                    self.device.raw().destroy_bind_group_layout(raw);
760                }
761            }
762            RawBindGroupLayout::RefDeviceEmptyBGL => {}
763        }
764    }
765}
766
767crate::impl_resource_type!(BindGroupLayout);
768crate::impl_labeled!(BindGroupLayout);
769crate::impl_parent_device!(BindGroupLayout);
770crate::impl_storage_item!(BindGroupLayout);
771
772impl BindGroupLayout {
773    pub(crate) fn raw(&self) -> &dyn hal::DynBindGroupLayout {
774        match &self.raw {
775            RawBindGroupLayout::Owning(raw) => raw.as_ref(),
776            RawBindGroupLayout::RefDeviceEmptyBGL => self.device.empty_bgl.as_ref(),
777        }
778    }
779
780    fn empty(device: &Arc<Device>) -> Arc<Self> {
781        let exclusive_pipeline = crate::OnceCellOrLock::new();
782        exclusive_pipeline.set(ExclusivePipeline::None).unwrap();
783        Arc::new(Self {
784            raw: RawBindGroupLayout::RefDeviceEmptyBGL,
785            device: device.clone(),
786            entries: bgl::EntryMap::default(),
787            origin: bgl::Origin::Derived,
788            exclusive_pipeline,
789            binding_count_validator: BindingTypeMaxCountValidator::default(),
790            label: String::new(),
791        })
792    }
793}
794
795#[derive(Clone, Debug, Error)]
796#[non_exhaustive]
797pub enum CreatePipelineLayoutError {
798    #[error(transparent)]
799    Device(#[from] DeviceError),
800    #[error(
801        "Immediate data has range bound {size} which is not aligned to IMMEDIATE_DATA_ALIGNMENT ({})",
802        wgt::IMMEDIATE_DATA_ALIGNMENT
803    )]
804    MisalignedImmediateSize { size: u32 },
805    #[error(transparent)]
806    MissingFeatures(#[from] MissingFeatures),
807    #[error(
808        "Immediate data has size {size} which exceeds device immediate data size limit 0..{max}"
809    )]
810    ImmediateRangeTooLarge { size: u32, max: u32 },
811    #[error(transparent)]
812    TooManyBindings(BindingTypeMaxCountError),
813    #[error("Bind group layout count {actual} exceeds device bind group limit {max}")]
814    TooManyGroups { actual: usize, max: usize },
815    #[error(transparent)]
816    InvalidResource(#[from] InvalidResourceError),
817    #[error("Bind group layout at index {index} has an exclusive pipeline: {pipeline}")]
818    BglHasExclusivePipeline { index: usize, pipeline: String },
819}
820
821impl WebGpuError for CreatePipelineLayoutError {
822    fn webgpu_error_type(&self) -> ErrorType {
823        let e: &dyn WebGpuError = match self {
824            Self::Device(e) => e,
825            Self::MissingFeatures(e) => e,
826            Self::InvalidResource(e) => e,
827            Self::TooManyBindings(e) => e,
828            Self::MisalignedImmediateSize { .. }
829            | Self::ImmediateRangeTooLarge { .. }
830            | Self::TooManyGroups { .. }
831            | Self::BglHasExclusivePipeline { .. } => return ErrorType::Validation,
832        };
833        e.webgpu_error_type()
834    }
835}
836
837#[derive(Clone, Debug, Error)]
838#[non_exhaustive]
839pub enum ImmediateUploadError {
840    #[error("Provided immediate data written to offset {offset}..{end_offset} overruns the immediate data range with a size of {size}")]
841    TooLarge {
842        offset: u32,
843        end_offset: u32,
844        size: u32,
845    },
846    #[error(
847        "Provided immediate data offset {0} does not respect `IMMEDIATE_DATA_ALIGNMENT` ({ida})",
848        ida = wgt::IMMEDIATE_DATA_ALIGNMENT
849    )]
850    Unaligned(u32),
851}
852
853impl WebGpuError for ImmediateUploadError {
854    fn webgpu_error_type(&self) -> ErrorType {
855        ErrorType::Validation
856    }
857}
858
859/// Describes a pipeline layout.
860///
861/// A `PipelineLayoutDescriptor` can be used to create a pipeline layout.
862#[derive(Clone, Debug, PartialEq, Eq, Hash)]
863#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
864#[cfg_attr(feature = "serde", serde(bound = "BGL: Serialize"))]
865pub struct PipelineLayoutDescriptor<'a, BGL = BindGroupLayoutId>
866where
867    [Option<BGL>]: ToOwned,
868    <[Option<BGL>] as ToOwned>::Owned: fmt::Debug,
869{
870    /// Debug label of the pipeline layout.
871    ///
872    /// This will show up in graphics debuggers for easy identification.
873    pub label: Label<'a>,
874    /// Bind groups that this pipeline uses. The first entry will provide all the bindings for
875    /// "set = 0", second entry will provide all the bindings for "set = 1" etc.
876    #[cfg_attr(
877        feature = "serde",
878        serde(bound(deserialize = "<[Option<BGL>] as ToOwned>::Owned: Deserialize<'de>"))
879    )]
880    pub bind_group_layouts: Cow<'a, [Option<BGL>]>,
881    /// The number of bytes of immediate data that are allocated for use
882    /// in the shader. The `var<immediate>`s in the shader attached to
883    /// this pipeline must be equal or smaller than this size.
884    ///
885    /// If this value is non-zero, [`wgt::Features::IMMEDIATES`] must be enabled.
886    pub immediate_size: u32,
887}
888
889/// cbindgen:ignore
890pub type ResolvedPipelineLayoutDescriptor<'a, BGL = Arc<BindGroupLayout>> =
891    PipelineLayoutDescriptor<'a, BGL>;
892
893#[derive(Debug)]
894pub struct PipelineLayout {
895    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineLayout>>,
896    pub(crate) device: Arc<Device>,
897    /// The `label` from the descriptor used to create the resource.
898    pub(crate) label: String,
899    pub(crate) bind_group_layouts: ArrayVec<Option<Arc<BindGroupLayout>>, { hal::MAX_BIND_GROUPS }>,
900    pub(crate) immediate_size: u32,
901}
902
903impl Drop for PipelineLayout {
904    fn drop(&mut self) {
905        resource_log!("Destroy raw {}", self.error_ident());
906        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
907        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
908        unsafe {
909            self.device.raw().destroy_pipeline_layout(raw);
910        }
911    }
912}
913
914impl PipelineLayout {
915    pub(crate) fn raw(&self) -> &dyn hal::DynPipelineLayout {
916        self.raw.as_ref()
917    }
918
919    pub fn get_bind_group_layout(
920        self: &Arc<Self>,
921        index: u32,
922    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
923        let max_bind_groups = self.device.limits.max_bind_groups;
924        if index >= max_bind_groups {
925            return Err(GetBindGroupLayoutError::IndexOutOfRange {
926                index,
927                max: max_bind_groups,
928            });
929        }
930        Ok(self
931            .bind_group_layouts
932            .get(index as usize)
933            .cloned()
934            .flatten()
935            .unwrap_or_else(|| BindGroupLayout::empty(&self.device)))
936    }
937
938    pub(crate) fn get_bgl_entry(
939        &self,
940        group: u32,
941        binding: u32,
942    ) -> Option<&wgt::BindGroupLayoutEntry> {
943        let bgl = self.bind_group_layouts.get(group as usize)?;
944        let bgl = bgl.as_ref()?;
945        bgl.entries.get(binding)
946    }
947
948    /// Validate immediates match up with expected ranges.
949    pub(crate) fn validate_immediates_ranges(
950        &self,
951        offset: u32,
952        end_offset: u32,
953    ) -> Result<(), ImmediateUploadError> {
954        // Don't need to validate size against the immediate data size limit here,
955        // as immediate data ranges are already validated to be within bounds,
956        // and we validate that they are within the ranges.
957
958        if !offset.is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT) {
959            return Err(ImmediateUploadError::Unaligned(offset));
960        }
961
962        if end_offset > self.immediate_size {
963            return Err(ImmediateUploadError::TooLarge {
964                offset,
965                end_offset,
966                size: self.immediate_size,
967            });
968        }
969        Ok(())
970    }
971}
972
973crate::impl_resource_type!(PipelineLayout);
974crate::impl_labeled!(PipelineLayout);
975crate::impl_parent_device!(PipelineLayout);
976crate::impl_storage_item!(PipelineLayout);
977
978#[repr(C)]
979#[derive(Clone, Debug, Hash, Eq, PartialEq)]
980#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
981pub struct BufferBinding<B = BufferId> {
982    pub buffer: B,
983    pub offset: wgt::BufferAddress,
984
985    /// Size of the binding. If `None`, the binding spans from `offset` to the
986    /// end of the buffer.
987    ///
988    /// We use `BufferAddress` to allow a size of zero on this `wgpu_core` type,
989    /// because JavaScript bindings cannot readily express `Option<NonZeroU64>`.
990    /// The `wgpu` API uses `Option<BufferSize>` (i.e. `NonZeroU64`) for this
991    /// field.
992    pub size: Option<wgt::BufferAddress>,
993}
994
995pub type ResolvedBufferBinding = BufferBinding<Arc<Buffer>>;
996
997// Note: Duplicated in `wgpu-rs` as `BindingResource`
998// They're different enough that it doesn't make sense to share a common type
999#[derive(Debug, Clone)]
1000#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1001pub enum BindingResource<
1002    'a,
1003    B = BufferId,
1004    S = SamplerId,
1005    TV = TextureViewId,
1006    TLAS = TlasId,
1007    ET = ExternalTextureId,
1008> where
1009    [BufferBinding<B>]: ToOwned,
1010    [S]: ToOwned,
1011    [TV]: ToOwned,
1012    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
1013    <[S] as ToOwned>::Owned: fmt::Debug,
1014    <[TV] as ToOwned>::Owned: fmt::Debug,
1015{
1016    Buffer(BufferBinding<B>),
1017    #[cfg_attr(
1018        feature = "serde",
1019        serde(bound(deserialize = "<[BufferBinding<B>] as ToOwned>::Owned: Deserialize<'de>"))
1020    )]
1021    BufferArray(Cow<'a, [BufferBinding<B>]>),
1022    Sampler(S),
1023    #[cfg_attr(
1024        feature = "serde",
1025        serde(bound(deserialize = "<[S] as ToOwned>::Owned: Deserialize<'de>"))
1026    )]
1027    SamplerArray(Cow<'a, [S]>),
1028    TextureView(TV),
1029    #[cfg_attr(
1030        feature = "serde",
1031        serde(bound(deserialize = "<[TV] as ToOwned>::Owned: Deserialize<'de>"))
1032    )]
1033    TextureViewArray(Cow<'a, [TV]>),
1034    AccelerationStructure(TLAS),
1035    ExternalTexture(ET),
1036}
1037
1038pub type ResolvedBindingResource<'a> = BindingResource<
1039    'a,
1040    Arc<Buffer>,
1041    Arc<Sampler>,
1042    Arc<TextureView>,
1043    Arc<Tlas>,
1044    Arc<ExternalTexture>,
1045>;
1046
1047#[derive(Clone, Debug, Error)]
1048#[non_exhaustive]
1049pub enum BindError {
1050    #[error(
1051        "Dynamic offsets not expected with null bind group at index {group}. However {actual} dynamic offset{s1} were provided.",
1052        s1 = if *.actual >= 2 { "s" } else { "" },
1053    )]
1054    DynamicOffsetCountNotZero { group: u32, actual: usize },
1055    #[error(
1056        "{bind_group} {group} expects {expected} dynamic offset{s0}. However {actual} dynamic offset{s1} were provided.",
1057        s0 = if *.expected >= 2 { "s" } else { "" },
1058        s1 = if *.actual >= 2 { "s" } else { "" },
1059    )]
1060    MismatchedDynamicOffsetCount {
1061        bind_group: ResourceErrorIdent,
1062        group: u32,
1063        actual: usize,
1064        expected: usize,
1065    },
1066    #[error(
1067        "Dynamic binding index {idx} (targeting {bind_group} {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}"
1068    )]
1069    UnalignedDynamicBinding {
1070        bind_group: ResourceErrorIdent,
1071        idx: usize,
1072        group: u32,
1073        binding: u32,
1074        offset: u32,
1075        alignment: u32,
1076        limit_name: &'static str,
1077    },
1078    #[error(
1079        "Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to {bind_group} {group} -> binding {binding}. \
1080         Buffer size is {buffer_size} bytes, the binding binds bytes {binding_range:?}, meaning the maximum the binding can be offset is {maximum_dynamic_offset} bytes",
1081    )]
1082    DynamicBindingOutOfBounds {
1083        bind_group: ResourceErrorIdent,
1084        idx: usize,
1085        group: u32,
1086        binding: u32,
1087        offset: u32,
1088        buffer_size: wgt::BufferAddress,
1089        binding_range: Range<wgt::BufferAddress>,
1090        maximum_dynamic_offset: wgt::BufferAddress,
1091    },
1092}
1093
1094impl WebGpuError for BindError {
1095    fn webgpu_error_type(&self) -> ErrorType {
1096        ErrorType::Validation
1097    }
1098}
1099
1100#[derive(Debug)]
1101pub struct BindGroupDynamicBindingData {
1102    /// The index of the binding.
1103    ///
1104    /// Used for more descriptive errors.
1105    pub(crate) binding_idx: u32,
1106    /// The size of the buffer.
1107    ///
1108    /// Used for more descriptive errors.
1109    pub(crate) buffer_size: wgt::BufferAddress,
1110    /// The range that the binding covers.
1111    ///
1112    /// Used for more descriptive errors.
1113    pub(crate) binding_range: Range<wgt::BufferAddress>,
1114    /// The maximum value the dynamic offset can have before running off the end of the buffer.
1115    pub(crate) maximum_dynamic_offset: wgt::BufferAddress,
1116    /// The binding type.
1117    pub(crate) binding_type: wgt::BufferBindingType,
1118}
1119
1120pub(crate) fn buffer_binding_type_alignment(
1121    limits: &wgt::Limits,
1122    binding_type: wgt::BufferBindingType,
1123) -> (u32, &'static str) {
1124    match binding_type {
1125        wgt::BufferBindingType::Uniform => (
1126            limits.min_uniform_buffer_offset_alignment,
1127            "min_uniform_buffer_offset_alignment",
1128        ),
1129        wgt::BufferBindingType::Storage { .. } => (
1130            limits.min_storage_buffer_offset_alignment,
1131            "min_storage_buffer_offset_alignment",
1132        ),
1133    }
1134}
1135
1136pub(crate) fn buffer_binding_type_bounds_check_alignment(
1137    alignments: &hal::Alignments,
1138    binding_type: wgt::BufferBindingType,
1139) -> wgt::BufferAddress {
1140    match binding_type {
1141        wgt::BufferBindingType::Uniform => alignments.uniform_bounds_check_alignment.get(),
1142        wgt::BufferBindingType::Storage { .. } => wgt::COPY_BUFFER_ALIGNMENT,
1143    }
1144}
1145
1146#[derive(Debug)]
1147pub(crate) struct BindGroupLateBufferBindingInfo {
1148    /// The normal binding index in the bind group.
1149    pub binding_index: u32,
1150    /// The size that exists at bind time.
1151    pub size: wgt::BufferSize,
1152}
1153
1154#[derive(Debug)]
1155pub struct BindGroup {
1156    pub(crate) raw: Snatchable<Box<dyn hal::DynBindGroup>>,
1157    pub(crate) device: Arc<Device>,
1158    pub(crate) layout: Arc<BindGroupLayout>,
1159    /// The `label` from the descriptor used to create the resource.
1160    pub(crate) label: String,
1161    pub(crate) tracking_data: TrackingData,
1162    pub(crate) used: BindGroupStates,
1163    pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,
1164    pub(crate) used_texture_ranges: Vec<TextureInitTrackerAction>,
1165    pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
1166    /// Actual binding sizes for buffers that don't have `min_binding_size`
1167    /// specified in BGL. Listed in the order of iteration of `BGL.entries`.
1168    pub(crate) late_buffer_binding_infos: Vec<BindGroupLateBufferBindingInfo>,
1169}
1170
1171impl Drop for BindGroup {
1172    fn drop(&mut self) {
1173        if let Some(raw) = self.raw.take() {
1174            resource_log!("Destroy raw {}", self.error_ident());
1175            unsafe {
1176                self.device.raw().destroy_bind_group(raw);
1177            }
1178        }
1179    }
1180}
1181
1182impl BindGroup {
1183    pub(crate) fn try_raw<'a>(
1184        &'a self,
1185        guard: &'a SnatchGuard,
1186    ) -> Result<&'a dyn hal::DynBindGroup, DestroyedResourceError> {
1187        // Clippy insist on writing it this way. The idea is to return None
1188        // if any of the raw buffer is not valid anymore.
1189        for buffer in &self.used_buffer_ranges {
1190            buffer.buffer.try_raw(guard)?;
1191        }
1192        for texture in &self.used_texture_ranges {
1193            texture.texture.try_raw(guard)?;
1194        }
1195
1196        self.raw
1197            .get(guard)
1198            .map(|raw| raw.as_ref())
1199            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1200    }
1201
1202    pub(crate) fn validate_dynamic_bindings(
1203        &self,
1204        bind_group_index: u32,
1205        offsets: &[wgt::DynamicOffset],
1206    ) -> Result<(), BindError> {
1207        if self.dynamic_binding_info.len() != offsets.len() {
1208            return Err(BindError::MismatchedDynamicOffsetCount {
1209                bind_group: self.error_ident(),
1210                group: bind_group_index,
1211                expected: self.dynamic_binding_info.len(),
1212                actual: offsets.len(),
1213            });
1214        }
1215
1216        for (idx, (info, &offset)) in self
1217            .dynamic_binding_info
1218            .iter()
1219            .zip(offsets.iter())
1220            .enumerate()
1221        {
1222            let (alignment, limit_name) =
1223                buffer_binding_type_alignment(&self.device.limits, info.binding_type);
1224            if !(offset as wgt::BufferAddress).is_multiple_of(alignment as u64) {
1225                return Err(BindError::UnalignedDynamicBinding {
1226                    bind_group: self.error_ident(),
1227                    group: bind_group_index,
1228                    binding: info.binding_idx,
1229                    idx,
1230                    offset,
1231                    alignment,
1232                    limit_name,
1233                });
1234            }
1235
1236            if offset as wgt::BufferAddress > info.maximum_dynamic_offset {
1237                return Err(BindError::DynamicBindingOutOfBounds {
1238                    bind_group: self.error_ident(),
1239                    group: bind_group_index,
1240                    binding: info.binding_idx,
1241                    idx,
1242                    offset,
1243                    buffer_size: info.buffer_size,
1244                    binding_range: info.binding_range.clone(),
1245                    maximum_dynamic_offset: info.maximum_dynamic_offset,
1246                });
1247            }
1248        }
1249
1250        Ok(())
1251    }
1252}
1253
1254crate::impl_resource_type!(BindGroup);
1255crate::impl_labeled!(BindGroup);
1256crate::impl_parent_device!(BindGroup);
1257crate::impl_storage_item!(BindGroup);
1258crate::impl_trackable!(BindGroup);
1259
1260#[derive(Clone, Debug, Error)]
1261#[non_exhaustive]
1262pub enum GetBindGroupLayoutError {
1263    #[error("Bind group layout index {index} is greater than the device's configured `max_bind_groups` limit {max}")]
1264    IndexOutOfRange { index: u32, max: u32 },
1265    #[error(transparent)]
1266    InvalidResource(#[from] InvalidResourceError),
1267}
1268
1269impl WebGpuError for GetBindGroupLayoutError {
1270    fn webgpu_error_type(&self) -> ErrorType {
1271        match self {
1272            Self::IndexOutOfRange { .. } => ErrorType::Validation,
1273            Self::InvalidResource(e) => e.webgpu_error_type(),
1274        }
1275    }
1276}
1277
1278#[derive(Clone, Debug, Error, Eq, PartialEq)]
1279#[error(
1280    "In bind group index {group_index}, the buffer bound at binding index {binding_index} \
1281     is bound with size {bound_size} where the shader expects {shader_size}."
1282)]
1283pub struct LateMinBufferBindingSizeMismatch {
1284    pub group_index: u32,
1285    pub binding_index: u32,
1286    pub shader_size: wgt::BufferAddress,
1287    pub bound_size: wgt::BufferAddress,
1288}