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