Skip to main content

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::{
9    fmt,
10    mem::ManuallyDrop,
11    num::{NonZeroU32, Saturating},
12    ops::Range,
13};
14
15use arrayvec::ArrayVec;
16use thiserror::Error;
17
18#[cfg(feature = "serde")]
19use serde::Deserialize;
20#[cfg(feature = "serde")]
21use serde::Serialize;
22
23use wgpu_sync::OnceCell;
24use wgt::error::{ErrorType, WebGpuError};
25
26use crate::{
27    api_log,
28    device::{bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures},
29    init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
30    pipeline::{ComputePipeline, RenderPipeline},
31    resource::{
32        Buffer, DestroyedResourceError, ExternalTexture, InvalidOrDestroyedResourceError,
33        InvalidResourceError, Labeled, MissingBufferUsageError, MissingTextureUsageError,
34        RawResourceAccess, ResourceErrorIdent, ResourceState, Sampler, TextureView, Tlas,
35        TrackingData,
36    },
37    resource_log,
38    snatch::{SnatchGuard, Snatchable},
39    track::{BindGroupStates, ResourceUsageCompatibilityError},
40    Label,
41};
42
43#[derive(Clone, Debug, Error)]
44#[non_exhaustive]
45pub enum BindGroupLayoutEntryError {
46    #[error("Multiple binding types provided, expected exactly one")]
47    MultipleBindingTypesProvided,
48    #[error("No binding types provided, expected exactly one")]
49    NoBindingTypesProvided,
50    #[error("Cube dimension is not expected for texture storage")]
51    StorageTextureCube,
52    #[error("Atomic storage textures are not allowed by baseline webgpu, they require the native only feature TEXTURE_ATOMIC")]
53    StorageTextureAtomic,
54    #[error("Arrays of bindings unsupported for this type of binding")]
55    ArrayUnsupported,
56    #[error("Multisampled binding with sample type `TextureSampleType::Float` must have filterable set to false.")]
57    SampleTypeFloatFilterableBindingMultisampled,
58    #[error("Multisampled texture binding view dimension must be 2d, got {0:?}")]
59    Non2DMultisampled(wgt::TextureViewDimension),
60    #[error(transparent)]
61    MissingFeatures(#[from] MissingFeatures),
62    #[error(transparent)]
63    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
64}
65
66#[derive(Clone, Debug, Error)]
67#[non_exhaustive]
68pub enum CreateBindGroupLayoutError {
69    #[error(transparent)]
70    Device(#[from] DeviceError),
71    #[error("Conflicting binding at index {0}")]
72    ConflictBinding(u32),
73    #[error("Binding {binding} entry is invalid")]
74    Entry {
75        binding: u32,
76        #[source]
77        error: BindGroupLayoutEntryError,
78    },
79    #[error(transparent)]
80    TooManyBindings(BindingTypeMaxCountError),
81    #[error("Bind groups may not contain both a binding array and a dynamically offset buffer")]
82    ContainsBothBindingArrayAndDynamicOffsetArray,
83    #[error("Bind groups may not contain both a binding array and a uniform buffer")]
84    ContainsBothBindingArrayAndUniformBuffer,
85    #[error("Binding index {binding} is greater than the maximum number {maximum}")]
86    InvalidBindingIndex { binding: u32, maximum: u32 },
87    #[error("Invalid visibility {0:?}")]
88    InvalidVisibility(wgt::ShaderStages),
89    #[error("Binding index {binding}: {access:?} access to storage textures with format {format:?} is not supported")]
90    UnsupportedStorageTextureAccess {
91        binding: u32,
92        access: wgt::StorageTextureAccess,
93        format: wgt::TextureFormat,
94    },
95}
96
97impl WebGpuError for CreateBindGroupLayoutError {
98    fn webgpu_error_type(&self) -> ErrorType {
99        match self {
100            Self::Device(e) => e.webgpu_error_type(),
101
102            Self::ConflictBinding(_)
103            | Self::Entry { .. }
104            | Self::TooManyBindings(_)
105            | Self::InvalidBindingIndex { .. }
106            | Self::InvalidVisibility(_)
107            | Self::ContainsBothBindingArrayAndDynamicOffsetArray
108            | Self::ContainsBothBindingArrayAndUniformBuffer
109            | Self::UnsupportedStorageTextureAccess { .. } => ErrorType::Validation,
110        }
111    }
112}
113
114#[derive(Clone, Debug, Error)]
115#[non_exhaustive]
116pub enum BindingError {
117    #[error(transparent)]
118    DestroyedResource(#[from] DestroyedResourceError),
119    #[error("Buffer {buffer}: Binding with size {binding_size} at offset {offset} would overflow buffer size of {buffer_size}")]
120    BindingRangeTooLarge {
121        buffer: ResourceErrorIdent,
122        offset: wgt::BufferAddress,
123        binding_size: u64,
124        buffer_size: u64,
125    },
126    #[error("Buffer {buffer}: Binding offset {offset} is greater than buffer size {buffer_size}")]
127    BindingOffsetTooLarge {
128        buffer: ResourceErrorIdent,
129        offset: wgt::BufferAddress,
130        buffer_size: u64,
131    },
132    /// The supplied binding offset was equal to the buffer size.
133    ///
134    /// A binding offset equal to the buffer size is legal for vertex and index buffer
135    /// bindings, but not for storage/uniform buffer bindings, thus this error should only
136    /// be raised for the latter.
137    #[error("Buffer {buffer}: Binding offset {offset} must be strictly less than buffer size {buffer_size}")]
138    BindingOffsetEqualsSize {
139        buffer: ResourceErrorIdent,
140        offset: wgt::BufferAddress,
141        buffer_size: u64,
142    },
143    #[error("Unbinding vertex buffer at slot {slot} expects offset to be 0. However an offset of {offset} was provided.")]
144    UnbindingVertexBufferOffsetNotZero { slot: u32, offset: u64 },
145    #[error("Unbinding vertex buffer at slot {slot} expects size to be 0. However a size of {size} was provided.")]
146    UnbindingVertexBufferSizeNotZero { slot: u32, size: u64 },
147}
148
149impl WebGpuError for BindingError {
150    fn webgpu_error_type(&self) -> ErrorType {
151        match self {
152            Self::DestroyedResource(e) => e.webgpu_error_type(),
153            Self::BindingRangeTooLarge { .. }
154            | Self::BindingOffsetTooLarge { .. }
155            | Self::BindingOffsetEqualsSize { .. }
156            | BindingError::UnbindingVertexBufferOffsetNotZero { .. }
157            | BindingError::UnbindingVertexBufferSizeNotZero { .. } => ErrorType::Validation,
158        }
159    }
160}
161
162// TODO: there may be additional variants here that can be extracted into
163// `BindingError`.
164#[derive(Clone, Debug, Error)]
165#[non_exhaustive]
166pub enum CreateBindGroupError {
167    #[error(transparent)]
168    Device(#[from] DeviceError),
169    #[error(transparent)]
170    DestroyedResource(#[from] DestroyedResourceError),
171    #[error(transparent)]
172    BindingError(#[from] BindingError),
173    #[error(
174        "Binding count declared with at most {expected} items, but {actual} items were provided"
175    )]
176    BindingArrayPartialLengthMismatch { actual: usize, expected: usize },
177    #[error(
178        "Binding count declared with exactly {expected} items, but {actual} items were provided"
179    )]
180    BindingArrayLengthMismatch { actual: usize, expected: usize },
181    #[error("Array binding provided zero elements")]
182    BindingArrayZeroLength,
183    #[error("Binding size {actual} of {buffer} is less than minimum {min}")]
184    BindingSizeTooSmall {
185        buffer: ResourceErrorIdent,
186        actual: u64,
187        min: u64,
188    },
189    #[error("{0} binding size is zero")]
190    BindingZeroSize(ResourceErrorIdent),
191    #[error("Number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
192    BindingsNumMismatch { actual: usize, expected: usize },
193    #[error("Binding {0} is used at least twice in the descriptor")]
194    DuplicateBinding(u32),
195    #[error("Unable to find a corresponding declaration for the given binding {0}")]
196    MissingBindingDeclaration(u32),
197    #[error(transparent)]
198    MissingBufferUsage(#[from] MissingBufferUsageError),
199    #[error(transparent)]
200    MissingTextureUsage(#[from] MissingTextureUsageError),
201    #[error("Binding declared as a single item, but bind group is using it as an array")]
202    SingleBindingExpected,
203    #[error("Effective buffer binding size {size} for storage buffers is expected to align to {alignment}, but size is {size}")]
204    UnalignedEffectiveBufferBindingSizeForStorage { alignment: u32, size: u64 },
205    #[error("Buffer offset {0} does not respect device's requested `{1}` limit {2}")]
206    UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32),
207    #[error(
208        "Buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
209    )]
210    BufferRangeTooLarge {
211        binding: u32,
212        given: u64,
213        limit: u64,
214    },
215    #[error("Binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
216    WrongBindingType {
217        // Index of the binding
218        binding: u32,
219        // The type given to the function
220        actual: wgt::BindingType,
221        // Human-readable description of expected types
222        expected: &'static str,
223    },
224    #[error("Texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}")]
225    InvalidTextureMultisample {
226        binding: u32,
227        layout_multisampled: bool,
228        view_samples: u32,
229    },
230    #[error(
231        "Texture binding {} expects sample type {:?}, but was given a view with format {:?} (sample type {:?})",
232        binding,
233        layout_sample_type,
234        view_format,
235        view_sample_type
236    )]
237    InvalidTextureSampleType {
238        binding: u32,
239        layout_sample_type: wgt::TextureSampleType,
240        view_format: wgt::TextureFormat,
241        view_sample_type: wgt::TextureSampleType,
242    },
243    #[error("Texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}")]
244    InvalidTextureDimension {
245        binding: u32,
246        layout_dimension: wgt::TextureViewDimension,
247        view_dimension: wgt::TextureViewDimension,
248    },
249    #[error("Storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}")]
250    InvalidStorageTextureFormat {
251        binding: u32,
252        layout_format: wgt::TextureFormat,
253        view_format: wgt::TextureFormat,
254    },
255    #[error("Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
256    InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },
257    #[error("Storage texture bindings must have an identity swizzle, but given a view with swizzle = {swizzle:?}")]
258    InvalidStorageTextureSwizzle {
259        swizzle: wgt::TextureComponentSwizzle,
260    },
261    #[error("External texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
262    InvalidExternalTextureMipLevelCount { binding: u32, mip_level_count: u32 },
263    #[error("External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = {format:?} at binding {binding}")]
264    InvalidExternalTextureFormat {
265        binding: u32,
266        format: wgt::TextureFormat,
267    },
268    #[error("Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}")]
269    WrongSamplerComparison {
270        binding: u32,
271        layout_cmp: bool,
272        sampler_cmp: bool,
273    },
274    #[error("Sampler binding {binding} expects filtering = {layout_flt}, but given a sampler with filtering = {sampler_flt}")]
275    WrongSamplerFiltering {
276        binding: u32,
277        layout_flt: bool,
278        sampler_flt: bool,
279    },
280    #[error("TLAS binding {binding} is required to support vertex returns but is missing flag AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN")]
281    MissingTLASVertexReturn { binding: u32 },
282    #[error("Bound texture views can not have both depth and stencil aspects enabled")]
283    DepthStencilAspect,
284    #[error(transparent)]
285    ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
286    #[error(transparent)]
287    InvalidResource(#[from] InvalidResourceError),
288}
289
290impl WebGpuError for CreateBindGroupError {
291    fn webgpu_error_type(&self) -> ErrorType {
292        match self {
293            Self::Device(e) => e.webgpu_error_type(),
294            Self::DestroyedResource(e) => e.webgpu_error_type(),
295            Self::BindingError(e) => e.webgpu_error_type(),
296            Self::MissingBufferUsage(e) => e.webgpu_error_type(),
297            Self::MissingTextureUsage(e) => e.webgpu_error_type(),
298            Self::ResourceUsageCompatibility(e) => e.webgpu_error_type(),
299            Self::InvalidResource(e) => e.webgpu_error_type(),
300            Self::BindingArrayPartialLengthMismatch { .. }
301            | Self::BindingArrayLengthMismatch { .. }
302            | Self::BindingArrayZeroLength
303            | Self::BindingSizeTooSmall { .. }
304            | Self::BindingsNumMismatch { .. }
305            | Self::BindingZeroSize(_)
306            | Self::DuplicateBinding(_)
307            | Self::MissingBindingDeclaration(_)
308            | Self::SingleBindingExpected
309            | Self::UnalignedEffectiveBufferBindingSizeForStorage { .. }
310            | Self::UnalignedBufferOffset(_, _, _)
311            | Self::BufferRangeTooLarge { .. }
312            | Self::WrongBindingType { .. }
313            | Self::InvalidTextureMultisample { .. }
314            | Self::InvalidTextureSampleType { .. }
315            | Self::InvalidTextureDimension { .. }
316            | Self::InvalidStorageTextureFormat { .. }
317            | Self::InvalidStorageTextureMipLevelCount { .. }
318            | Self::InvalidStorageTextureSwizzle { .. }
319            | Self::WrongSamplerComparison { .. }
320            | Self::WrongSamplerFiltering { .. }
321            | Self::DepthStencilAspect
322            | Self::MissingTLASVertexReturn { .. }
323            | Self::InvalidExternalTextureMipLevelCount { .. }
324            | Self::InvalidExternalTextureFormat { .. } => ErrorType::Validation,
325        }
326    }
327}
328
329#[derive(Clone, Debug, Error)]
330pub enum BindingZone {
331    #[error("Stage {0:?}")]
332    Stage(wgt::ShaderStages),
333    #[error("Whole pipeline")]
334    Pipeline,
335}
336
337#[derive(Clone, Debug, Error)]
338#[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())]
339pub struct BindingTypeMaxCountError {
340    pub kind: BindingTypeMaxCountErrorKind,
341    pub zone: BindingZone,
342    pub limit: u32,
343    pub count: u32,
344}
345
346impl WebGpuError for BindingTypeMaxCountError {
347    fn webgpu_error_type(&self) -> ErrorType {
348        ErrorType::Validation
349    }
350}
351
352#[derive(Clone, Debug)]
353pub enum BindingTypeMaxCountErrorKind {
354    DynamicUniformBuffers,
355    DynamicStorageBuffers,
356    SampledTextures,
357    Samplers,
358    StorageBuffers,
359    StorageTextures,
360    UniformBuffers,
361    BindingArrayElements,
362    BindingArraySamplerElements,
363    BindingArrayAccelerationStructureElements,
364    AccelerationStructures,
365    BuffersAndAccelerationStructures,
366}
367
368impl BindingTypeMaxCountErrorKind {
369    fn to_config_str(&self) -> &'static str {
370        match self {
371            BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {
372                "max_dynamic_uniform_buffers_per_pipeline_layout"
373            }
374            BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {
375                "max_dynamic_storage_buffers_per_pipeline_layout"
376            }
377            BindingTypeMaxCountErrorKind::SampledTextures => {
378                "max_sampled_textures_per_shader_stage"
379            }
380            BindingTypeMaxCountErrorKind::Samplers => "max_samplers_per_shader_stage",
381            BindingTypeMaxCountErrorKind::StorageBuffers => "max_storage_buffers_per_shader_stage",
382            BindingTypeMaxCountErrorKind::StorageTextures => {
383                "max_storage_textures_per_shader_stage"
384            }
385            BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
386            BindingTypeMaxCountErrorKind::BindingArrayElements => {
387                "max_binding_array_elements_per_shader_stage"
388            }
389            BindingTypeMaxCountErrorKind::BindingArraySamplerElements => {
390                "max_binding_array_sampler_elements_per_shader_stage"
391            }
392            BindingTypeMaxCountErrorKind::BindingArrayAccelerationStructureElements => {
393                "max_binding_array_acceleration_structure_elements_per_shader_stage"
394            }
395            BindingTypeMaxCountErrorKind::AccelerationStructures => {
396                "max_acceleration_structures_per_shader_stage"
397            }
398            BindingTypeMaxCountErrorKind::BuffersAndAccelerationStructures => {
399                "max_buffers_and_acceleration_structures_per_shader_stage"
400            }
401        }
402    }
403}
404
405#[derive(Debug, Default)]
406pub(crate) struct PerStageBindingTypeCounter {
407    vertex: Saturating<u32>,
408    fragment: Saturating<u32>,
409    compute: Saturating<u32>,
410}
411
412impl PerStageBindingTypeCounter {
413    pub(crate) fn add(&mut self, stage: wgt::ShaderStages, count: u32) {
414        if stage.contains(wgt::ShaderStages::VERTEX) {
415            self.vertex += count;
416        }
417        if stage.contains(wgt::ShaderStages::FRAGMENT) {
418            self.fragment += count;
419        }
420        if stage.contains(wgt::ShaderStages::COMPUTE) {
421            self.compute += count;
422        }
423    }
424
425    pub(crate) fn max(&self) -> (BindingZone, u32) {
426        let max_value = self.vertex.max(self.fragment.max(self.compute));
427        let mut stage = wgt::ShaderStages::NONE;
428        if max_value == self.vertex {
429            stage |= wgt::ShaderStages::VERTEX
430        }
431        if max_value == self.fragment {
432            stage |= wgt::ShaderStages::FRAGMENT
433        }
434        if max_value == self.compute {
435            stage |= wgt::ShaderStages::COMPUTE
436        }
437        (BindingZone::Stage(stage), max_value.0)
438    }
439
440    pub(crate) fn merge(&mut self, other: &Self) {
441        self.vertex += other.vertex;
442        self.fragment += other.fragment;
443        self.compute += other.compute;
444    }
445
446    pub(crate) fn validate(
447        &self,
448        limit: u32,
449        kind: BindingTypeMaxCountErrorKind,
450    ) -> Result<(), BindingTypeMaxCountError> {
451        let (zone, count) = self.max();
452        if limit < count {
453            Err(BindingTypeMaxCountError {
454                kind,
455                zone,
456                limit,
457                count,
458            })
459        } else {
460            Ok(())
461        }
462    }
463}
464
465#[derive(Debug, Default)]
466pub(crate) struct BindingTypeMaxCountValidator {
467    dynamic_uniform_buffers: u32,
468    dynamic_storage_buffers: u32,
469    sampled_textures: PerStageBindingTypeCounter,
470    samplers: PerStageBindingTypeCounter,
471    storage_buffers: PerStageBindingTypeCounter,
472    storage_textures: PerStageBindingTypeCounter,
473    uniform_buffers: PerStageBindingTypeCounter,
474    acceleration_structures: PerStageBindingTypeCounter,
475    binding_array_elements: PerStageBindingTypeCounter,
476    binding_array_sampler_elements: PerStageBindingTypeCounter,
477    binding_array_acceleration_structure_elements: PerStageBindingTypeCounter,
478    has_bindless_array: bool,
479}
480
481impl BindingTypeMaxCountValidator {
482    pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {
483        let count = binding.count.map_or(1, |count| count.get());
484
485        if binding.count.is_some() {
486            self.binding_array_elements.add(binding.visibility, count);
487            self.has_bindless_array = true;
488
489            match binding.ty {
490                wgt::BindingType::Sampler(_) => {
491                    self.binding_array_sampler_elements
492                        .add(binding.visibility, count);
493                }
494                wgt::BindingType::AccelerationStructure { .. } => {
495                    self.binding_array_acceleration_structure_elements
496                        .add(binding.visibility, count);
497                }
498                _ => {}
499            }
500        } else {
501            match binding.ty {
502                wgt::BindingType::Buffer {
503                    ty: wgt::BufferBindingType::Uniform,
504                    has_dynamic_offset,
505                    ..
506                } => {
507                    self.uniform_buffers.add(binding.visibility, count);
508                    if has_dynamic_offset {
509                        self.dynamic_uniform_buffers += count;
510                    }
511                }
512                wgt::BindingType::Buffer {
513                    ty: wgt::BufferBindingType::Storage { .. },
514                    has_dynamic_offset,
515                    ..
516                } => {
517                    self.storage_buffers.add(binding.visibility, count);
518                    if has_dynamic_offset {
519                        self.dynamic_storage_buffers += count;
520                    }
521                }
522                wgt::BindingType::Sampler { .. } => {
523                    self.samplers.add(binding.visibility, count);
524                }
525                wgt::BindingType::Texture { .. } => {
526                    self.sampled_textures.add(binding.visibility, count);
527                }
528                wgt::BindingType::StorageTexture { .. } => {
529                    self.storage_textures.add(binding.visibility, count);
530                }
531                wgt::BindingType::AccelerationStructure { .. } => {
532                    self.acceleration_structures.add(binding.visibility, count);
533                }
534                wgt::BindingType::ExternalTexture => {
535                    // https://www.w3.org/TR/webgpu/#gpuexternaltexture
536                    // In order to account for many possible representations,
537                    // the binding conservatively uses the following, for each
538                    // external texture:
539                    // * Three sampled textures for up to 3 planes
540                    // * One additional sampled texture for a 3D LUT
541                    // * One sampler to sample the LUT
542                    // * One uniform buffer for metadata
543                    self.sampled_textures.add(binding.visibility, count * 4);
544                    self.samplers.add(binding.visibility, count);
545                    self.uniform_buffers.add(binding.visibility, count);
546                }
547            }
548        }
549    }
550
551    pub(crate) fn merge(&mut self, other: &Self) {
552        self.dynamic_uniform_buffers += other.dynamic_uniform_buffers;
553        self.dynamic_storage_buffers += other.dynamic_storage_buffers;
554        self.sampled_textures.merge(&other.sampled_textures);
555        self.samplers.merge(&other.samplers);
556        self.storage_buffers.merge(&other.storage_buffers);
557        self.storage_textures.merge(&other.storage_textures);
558        self.uniform_buffers.merge(&other.uniform_buffers);
559        self.acceleration_structures
560            .merge(&other.acceleration_structures);
561        self.binding_array_elements
562            .merge(&other.binding_array_elements);
563        self.binding_array_sampler_elements
564            .merge(&other.binding_array_sampler_elements);
565        self.binding_array_acceleration_structure_elements
566            .merge(&other.binding_array_acceleration_structure_elements);
567    }
568
569    pub(crate) fn validate(
570        &self,
571        limits: &wgt::Limits,
572        instance_flags: wgt::InstanceFlags,
573    ) -> Result<(), BindingTypeMaxCountError> {
574        if limits.max_dynamic_uniform_buffers_per_pipeline_layout < self.dynamic_uniform_buffers {
575            return Err(BindingTypeMaxCountError {
576                kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,
577                zone: BindingZone::Pipeline,
578                limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,
579                count: self.dynamic_uniform_buffers,
580            });
581        }
582        if limits.max_dynamic_storage_buffers_per_pipeline_layout < self.dynamic_storage_buffers {
583            return Err(BindingTypeMaxCountError {
584                kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,
585                zone: BindingZone::Pipeline,
586                limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,
587                count: self.dynamic_storage_buffers,
588            });
589        }
590        self.sampled_textures.validate(
591            limits.max_sampled_textures_per_shader_stage,
592            BindingTypeMaxCountErrorKind::SampledTextures,
593        )?;
594        self.samplers.validate(
595            limits.max_samplers_per_shader_stage,
596            BindingTypeMaxCountErrorKind::Samplers,
597        )?;
598        self.storage_buffers.validate(
599            limits.max_storage_buffers_per_shader_stage,
600            BindingTypeMaxCountErrorKind::StorageBuffers,
601        )?;
602        // NOTE: We don't explicitly check `max_storage_buffers_in_vertex_stage` or
603        // `max_storage_buffers_in_fragment_stage` because we don't assign different values between
604        // those and `max_storage_buffers_per_shader_stage`. If this changes, this needs to be
605        // fixed!
606        self.storage_textures.validate(
607            limits.max_storage_textures_per_shader_stage,
608            BindingTypeMaxCountErrorKind::StorageTextures,
609        )?;
610        // NOTE: We don't explicitly check `max_storage_textures_in_vertex_stage` or
611        // `max_storage_textures_in_fragment_stage` because we don't assign different values between
612        // those and `max_storage_textures_per_shader_stage`. If this changes, this needs to be
613        // fixed!
614        self.uniform_buffers.validate(
615            limits.max_uniform_buffers_per_shader_stage,
616            BindingTypeMaxCountErrorKind::UniformBuffers,
617        )?;
618        self.binding_array_elements.validate(
619            limits.max_binding_array_elements_per_shader_stage,
620            BindingTypeMaxCountErrorKind::BindingArrayElements,
621        )?;
622        self.binding_array_sampler_elements.validate(
623            limits.max_binding_array_sampler_elements_per_shader_stage,
624            BindingTypeMaxCountErrorKind::BindingArraySamplerElements,
625        )?;
626        self.binding_array_acceleration_structure_elements
627            .validate(
628                limits.max_binding_array_acceleration_structure_elements_per_shader_stage,
629                BindingTypeMaxCountErrorKind::BindingArrayAccelerationStructureElements,
630            )?;
631        self.acceleration_structures.validate(
632            limits.max_acceleration_structures_per_shader_stage,
633            BindingTypeMaxCountErrorKind::AccelerationStructures,
634        )?;
635
636        if !instance_flags.contains(wgt::InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
637            self.buffers_and_acceleration_structures().validate(
638                limits.max_buffers_and_acceleration_structures_per_shader_stage,
639                BindingTypeMaxCountErrorKind::BuffersAndAccelerationStructures,
640            )?;
641        }
642
643        Ok(())
644    }
645
646    fn buffers_and_acceleration_structures(&self) -> PerStageBindingTypeCounter {
647        let mut buffers_and_acceleration_structures = PerStageBindingTypeCounter::default();
648        buffers_and_acceleration_structures.merge(&self.uniform_buffers);
649        buffers_and_acceleration_structures.merge(&self.storage_buffers);
650        buffers_and_acceleration_structures.merge(&self.acceleration_structures);
651        buffers_and_acceleration_structures
652    }
653
654    pub(crate) fn buffers_and_acceleration_structures_in_vertex_stage(&self) -> u32 {
655        self.buffers_and_acceleration_structures().vertex.0
656    }
657
658    /// Validate that the bind group layout does not contain both a binding array and a dynamic offset array.
659    ///
660    /// This allows us to use `UPDATE_AFTER_BIND` on vulkan for bindless arrays. Vulkan does not allow
661    /// `UPDATE_AFTER_BIND` on dynamic offset arrays. See <https://github.com/gfx-rs/wgpu/issues/6737>
662    pub(crate) fn validate_binding_arrays(&self) -> Result<(), CreateBindGroupLayoutError> {
663        let has_dynamic_offset_array =
664            self.dynamic_uniform_buffers > 0 || self.dynamic_storage_buffers > 0;
665        let has_uniform_buffer = self.uniform_buffers.max().1 > 0;
666        if self.has_bindless_array && has_dynamic_offset_array {
667            return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndDynamicOffsetArray);
668        }
669        if self.has_bindless_array && has_uniform_buffer {
670            return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndUniformBuffer);
671        }
672        Ok(())
673    }
674}
675
676/// Bindable resource and the slot to bind it to.
677/// cbindgen:ignore
678#[derive(Clone, Debug)]
679#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
680pub struct BindGroupEntry<
681    'a,
682    B = Arc<Buffer>,
683    S = Arc<Sampler>,
684    TV = Arc<TextureView>,
685    TLAS = Arc<Tlas>,
686    ET = Arc<ExternalTexture>,
687> where
688    [BufferBinding<B>]: ToOwned,
689    [S]: ToOwned,
690    [TV]: ToOwned,
691    [TLAS]: ToOwned,
692    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
693    <[S] as ToOwned>::Owned: fmt::Debug,
694    <[TV] as ToOwned>::Owned: fmt::Debug,
695    <[TLAS] as ToOwned>::Owned: fmt::Debug,
696{
697    /// Slot for which binding provides resource. Corresponds to an entry of the same
698    /// binding index in the [`BindGroupLayoutDescriptor`].
699    pub binding: u32,
700    #[cfg_attr(
701        feature = "serde",
702        serde(bound(deserialize = "BindingResource<'a, B, S, TV, TLAS, ET>: Deserialize<'de>"))
703    )]
704    /// Resource to attach to the binding
705    pub resource: BindingResource<'a, B, S, TV, TLAS, ET>,
706}
707
708/// Describes a group of bindings and the resources to be bound.
709#[derive(Clone, Debug)]
710#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
711/// cbindgen:ignore
712pub struct BindGroupDescriptor<
713    'a,
714    BGL = Arc<BindGroupLayout>,
715    B = Arc<Buffer>,
716    S = Arc<Sampler>,
717    TV = Arc<TextureView>,
718    TLAS = Arc<Tlas>,
719    ET = Arc<ExternalTexture>,
720> where
721    [BufferBinding<B>]: ToOwned,
722    [S]: ToOwned,
723    [TV]: ToOwned,
724    [TLAS]: ToOwned,
725    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
726    <[S] as ToOwned>::Owned: fmt::Debug,
727    <[TV] as ToOwned>::Owned: fmt::Debug,
728    <[TLAS] as ToOwned>::Owned: fmt::Debug,
729    [BindGroupEntry<'a, B, S, TV, TLAS, ET>]: ToOwned,
730    <[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: fmt::Debug,
731{
732    /// Debug label of the bind group.
733    ///
734    /// This will show up in graphics debuggers for easy identification.
735    pub label: Label<'a>,
736    /// The [`BindGroupLayout`] that corresponds to this bind group.
737    pub layout: BGL,
738    #[cfg_attr(
739        feature = "serde",
740        serde(bound(
741            deserialize = "<[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: Deserialize<'de>"
742        ))
743    )]
744    /// The resources to bind to this bind group.
745    #[allow(clippy::type_complexity)]
746    pub entries: Cow<'a, [BindGroupEntry<'a, B, S, TV, TLAS, ET>]>,
747}
748
749/// A buffer binding.
750///
751/// Corresponds to [WebGPU `GPUBufferBindingLayout`](
752/// https://gpuweb.github.io/gpuweb/#dictdef-gpubufferbindinglayout).
753#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
754#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
755pub struct BufferBindingLayout {
756    /// Sub-type of the buffer binding.
757    pub ty: wgt::BufferBindingType,
758
759    /// Indicates that the binding has a dynamic offset.
760    ///
761    /// One offset must be passed to [`RenderPass::set_bind_group`](crate::command::RenderPass::set_bind_group)
762    /// for each dynamic binding in increasing order of binding number.
763    ///
764    #[cfg_attr(feature = "serde", serde(default))]
765    pub has_dynamic_offset: bool,
766
767    /// The minimum size for a [`BufferBinding`] matching this entry, in bytes.
768    ///
769    /// If this is `Some(size)`:
770    ///
771    /// - When calling [`Device::create_bind_group`], the resource at this bind point
772    ///   must be a [`BindingResource::Buffer`] whose effective size is at
773    ///   least `size`.
774    ///
775    /// - When calling [`Device::create_render_pipeline`] or [`Device::create_compute_pipeline`],
776    ///   `size` must be at least the [minimum buffer binding size] for the
777    ///   shader module global at this bind point: large enough to hold the
778    ///   global's value, along with one element of a trailing runtime-sized
779    ///   array, if present.
780    ///
781    /// If this is `None`:
782    ///
783    /// - Each draw or dispatch command checks that the buffer range at this
784    ///   bind point satisfies the [minimum buffer binding size].
785    ///
786    /// [minimum buffer binding size]: https://www.w3.org/TR/webgpu/#minimum-buffer-binding-size
787    #[cfg_attr(feature = "serde", serde(default))]
788    pub min_binding_size: Option<wgt::BufferSize>,
789}
790
791/// A sampler that can be used to sample a texture.
792///
793/// Example WGSL syntax:
794/// ```rust,ignore
795/// @group(0) @binding(0)
796/// var s: sampler;
797/// ```
798///
799/// Example GLSL syntax:
800/// ```cpp,ignore
801/// layout(binding = 0)
802/// uniform sampler s;
803/// ```
804///
805/// Corresponds to [WebGPU `GPUSamplerBindingLayout`](
806/// https://gpuweb.github.io/gpuweb/#dictdef-gpusamplerbindinglayout).
807#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
808#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
809pub struct SamplerBindingLayout {
810    /// Type of a sampler binding.
811    pub ty: wgt::SamplerBindingType,
812}
813
814/// A texture binding.
815///
816/// Example WGSL syntax:
817/// ```rust,ignore
818/// @group(0) @binding(0)
819/// var t: texture_2d<f32>;
820/// ```
821///
822/// Example GLSL syntax:
823/// ```cpp,ignore
824/// layout(binding = 0)
825/// uniform texture2D t;
826/// ```
827///
828/// Corresponds to [WebGPU `GPUTextureBindingLayout`](
829/// https://gpuweb.github.io/gpuweb/#dictdef-gputexturebindinglayout).
830#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
831#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
832pub struct TextureBindingLayout {
833    /// Sample type of the texture binding.
834    pub sample_type: wgt::TextureSampleType,
835    /// Dimension of the texture view that is going to be sampled.
836    pub view_dimension: wgt::TextureViewDimension,
837    /// True if the texture has a sample count greater than 1. If this is true,
838    /// the texture must be declared as `texture_multisampled_2d` or
839    /// `texture_depth_multisampled_2d` in the shader, and read using `textureLoad`.
840    pub multisampled: bool,
841}
842
843/// A storage texture.
844///
845/// Example WGSL syntax:
846/// ```rust,ignore
847/// @group(0) @binding(0)
848/// var my_storage_image: texture_storage_2d<r32float, write>;
849/// ```
850///
851/// Example GLSL syntax:
852/// ```cpp,ignore
853/// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage;
854/// ```
855/// Note that the texture format must be specified in the shader, along with the
856/// access mode. For WGSL, the format must be one of the enumerants in the list
857/// of [storage texel formats](https://gpuweb.github.io/gpuweb/wgsl/#storage-texel-formats).
858///
859/// Corresponds to [WebGPU `GPUStorageTextureBindingLayout`](
860/// https://gpuweb.github.io/gpuweb/#dictdef-gpustoragetexturebindinglayout).
861#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
862#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
863pub struct StorageTextureBindingLayout {
864    /// Allowed access to this texture.
865    pub access: wgt::StorageTextureAccess,
866    /// Format of the texture.
867    pub format: wgt::TextureFormat,
868    /// Dimension of the texture view that is going to be sampled.
869    pub view_dimension: wgt::TextureViewDimension,
870}
871
872/// A ray-tracing acceleration structure binding.
873///
874/// Example WGSL syntax:
875/// ```rust,ignore
876/// @group(0) @binding(0)
877/// var as: acceleration_structure;
878/// ```
879///
880/// or with vertex return enabled
881/// ```rust,ignore
882/// @group(0) @binding(0)
883/// var as: acceleration_structure<vertex_return>;
884/// ```
885///
886/// Example GLSL syntax:
887/// ```cpp,ignore
888/// layout(binding = 0)
889/// uniform accelerationStructureEXT as;
890/// ```
891#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
892#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
893pub struct AccelerationStructureBindingLayout {
894    /// Whether this acceleration structure can be used to
895    /// create a ray query that has flag vertex return in the shader
896    ///
897    /// If enabled requires [`wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN`]
898    pub vertex_return: bool,
899}
900
901/// An external texture binding.
902///
903/// Example WGSL syntax:
904/// ```rust,ignore
905/// @group(0) @binding(0)
906/// var t: texture_external;
907/// ```
908///
909/// Corresponds to [WebGPU `GPUExternalTextureBindingLayout`](
910/// https://gpuweb.github.io/gpuweb/#dictdef-gpuexternaltexturebindinglayout).
911///
912/// Requires [`wgt::Features::EXTERNAL_TEXTURE`]
913#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
914#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
915pub struct ExternalTextureBindingLayout;
916
917/// Describes a single binding inside a bind group.
918///
919/// Corresponds to [WebGPU `GPUBindGroupLayoutEntry`](
920/// https://gpuweb.github.io/gpuweb/#dictdef-gpubindgrouplayoutentry).
921#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
922#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
923pub struct BindGroupLayoutEntry {
924    /// Binding index. Must match shader index and be unique inside a `BindGroupLayout`. A binding
925    /// of index 1, would be described as `@group(0) @binding(1)` in shaders.
926    pub binding: u32,
927    /// Which shader stages can see this binding.
928    pub visibility: wgt::ShaderStages,
929
930    // type of binding
931    #[cfg_attr(feature = "serde", serde(default))]
932    pub buffer: Option<BufferBindingLayout>,
933    #[cfg_attr(feature = "serde", serde(default))]
934    pub sampler: Option<SamplerBindingLayout>,
935    #[cfg_attr(feature = "serde", serde(default))]
936    pub texture: Option<TextureBindingLayout>,
937    #[cfg_attr(feature = "serde", serde(default))]
938    pub storage_texture: Option<StorageTextureBindingLayout>,
939    #[cfg_attr(feature = "serde", serde(default))]
940    pub external_texture: Option<ExternalTextureBindingLayout>,
941    #[cfg_attr(feature = "serde", serde(default))]
942    pub acceleration_structure: Option<AccelerationStructureBindingLayout>,
943
944    /// If the binding is an array of multiple resources. Corresponds to `binding_array<T>` in the shader.
945    ///
946    /// When this is `Some` the following validation applies:
947    /// - Count must be of value 1 or greater, this corresponds to the length of the array of resources that will be bound.
948    /// - When `ty == BindingType::Texture`, [`wgt::Features::TEXTURE_BINDING_ARRAY`] must be supported.
949    /// - When `ty == BindingType::Sampler`, [`wgt::Features::TEXTURE_BINDING_ARRAY`] must be supported.
950    /// - When `ty == BindingType::Buffer`, [`wgt::Features::BUFFER_BINDING_ARRAY`] must be supported.
951    /// - When `ty == BindingType::Buffer` and `ty.ty == BufferBindingType::Storage`, [`wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY`] must be supported.
952    /// - When `ty == BindingType::StorageTexture`, [`wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY`] must be supported.
953    /// - When any binding in the group is an array, no `BindingType::Buffer` in the group may have `has_dynamic_offset == true`
954    /// - When any binding in the group is an array, no `BindingType::Buffer` in the group may have `ty.ty == BufferBindingType::Uniform`.
955    /// - If [`wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY`] is enabled, the specified count becomes the upper bound instead.
956    ///
957    #[cfg_attr(feature = "serde", serde(default))]
958    pub count: Option<NonZeroU32>,
959}
960
961impl From<core::convert::Infallible> for CreateBindGroupLayoutError {
962    fn from(_: core::convert::Infallible) -> Self {
963        unreachable!()
964    }
965}
966
967impl From<wgt::BindGroupLayoutEntry> for BindGroupLayoutEntry {
968    fn from(val: wgt::BindGroupLayoutEntry) -> Self {
969        let wgt::BindGroupLayoutEntry {
970            binding,
971            visibility,
972            ty,
973            count,
974        } = val;
975        BindGroupLayoutEntry {
976            binding,
977            visibility,
978            buffer: if let wgt::BindingType::Buffer {
979                ty,
980                has_dynamic_offset,
981                min_binding_size,
982            } = ty
983            {
984                Some(BufferBindingLayout {
985                    ty,
986                    has_dynamic_offset,
987                    min_binding_size,
988                })
989            } else {
990                None
991            },
992            sampler: if let wgt::BindingType::Sampler(ty) = ty {
993                Some(SamplerBindingLayout { ty })
994            } else {
995                None
996            },
997            texture: if let wgt::BindingType::Texture {
998                sample_type,
999                view_dimension,
1000                multisampled,
1001            } = ty
1002            {
1003                Some(TextureBindingLayout {
1004                    sample_type,
1005                    view_dimension,
1006                    multisampled,
1007                })
1008            } else {
1009                None
1010            },
1011            storage_texture: if let wgt::BindingType::StorageTexture {
1012                access,
1013                format,
1014                view_dimension,
1015            } = ty
1016            {
1017                Some(StorageTextureBindingLayout {
1018                    access,
1019                    format,
1020                    view_dimension,
1021                })
1022            } else {
1023                None
1024            },
1025            external_texture: if let wgt::BindingType::ExternalTexture = ty {
1026                Some(ExternalTextureBindingLayout {})
1027            } else {
1028                None
1029            },
1030            acceleration_structure: if let wgt::BindingType::AccelerationStructure {
1031                vertex_return,
1032            } = ty
1033            {
1034                Some(AccelerationStructureBindingLayout { vertex_return })
1035            } else {
1036                None
1037            },
1038            count,
1039        }
1040    }
1041}
1042
1043impl TryInto<wgt::BindGroupLayoutEntry> for BindGroupLayoutEntry {
1044    type Error = CreateBindGroupLayoutError;
1045
1046    fn try_into(self) -> Result<wgt::BindGroupLayoutEntry, Self::Error> {
1047        let BindGroupLayoutEntry {
1048            binding,
1049            visibility,
1050            buffer,
1051            sampler,
1052            texture,
1053            storage_texture,
1054            external_texture,
1055            acceleration_structure,
1056            count,
1057        } = self;
1058        let mut binding_ty: Option<wgt::BindingType> = None;
1059        if let Some(BufferBindingLayout {
1060            ty,
1061            has_dynamic_offset,
1062            min_binding_size,
1063        }) = buffer
1064        {
1065            binding_ty = Some(wgt::BindingType::Buffer {
1066                ty,
1067                has_dynamic_offset,
1068                min_binding_size,
1069            });
1070        }
1071        if let Some(SamplerBindingLayout { ty }) = sampler {
1072            if binding_ty.is_some() {
1073                return Err(CreateBindGroupLayoutError::Entry {
1074                    binding,
1075                    error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1076                });
1077            }
1078            binding_ty = Some(wgt::BindingType::Sampler(ty))
1079        }
1080        if let Some(TextureBindingLayout {
1081            sample_type,
1082            view_dimension,
1083            multisampled,
1084        }) = texture
1085        {
1086            if binding_ty.is_some() {
1087                return Err(CreateBindGroupLayoutError::Entry {
1088                    binding,
1089                    error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1090                });
1091            }
1092            binding_ty = Some(wgt::BindingType::Texture {
1093                sample_type,
1094                view_dimension,
1095                multisampled,
1096            })
1097        }
1098        if let Some(StorageTextureBindingLayout {
1099            access,
1100            format,
1101            view_dimension,
1102        }) = storage_texture
1103        {
1104            if binding_ty.is_some() {
1105                return Err(CreateBindGroupLayoutError::Entry {
1106                    binding,
1107                    error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1108                });
1109            }
1110            binding_ty = Some(wgt::BindingType::StorageTexture {
1111                access,
1112                format,
1113                view_dimension,
1114            })
1115        }
1116        if let Some(ExternalTextureBindingLayout) = external_texture {
1117            if binding_ty.is_some() {
1118                return Err(CreateBindGroupLayoutError::Entry {
1119                    binding,
1120                    error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1121                });
1122            }
1123            binding_ty = Some(wgt::BindingType::ExternalTexture)
1124        }
1125        if let Some(AccelerationStructureBindingLayout { vertex_return }) = acceleration_structure {
1126            if binding_ty.is_some() {
1127                return Err(CreateBindGroupLayoutError::Entry {
1128                    binding,
1129                    error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1130                });
1131            }
1132            binding_ty = Some(wgt::BindingType::AccelerationStructure { vertex_return })
1133        }
1134        Ok(wgt::BindGroupLayoutEntry {
1135            binding,
1136            visibility,
1137            ty: binding_ty.ok_or(CreateBindGroupLayoutError::Entry {
1138                binding,
1139                error: BindGroupLayoutEntryError::NoBindingTypesProvided,
1140            })?,
1141            count,
1142        })
1143    }
1144}
1145
1146/// Describes a [`BindGroupLayout`].
1147#[derive(Clone, Debug)]
1148#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1149pub struct BindGroupLayoutDescriptor<'a, BGLE: Copy> {
1150    /// Debug label of the bind group layout.
1151    ///
1152    /// This will show up in graphics debuggers for easy identification.
1153    pub label: Label<'a>,
1154    /// Array of entries in this BindGroupLayout
1155    pub entries: Cow<'a, [BGLE]>,
1156}
1157
1158/// Used by [`BindGroupLayout`]. It indicates whether the BGL must be
1159/// used with a specific pipeline. This constraint only happens when
1160/// the BGLs have been derived from a pipeline without a layout.
1161#[derive(Clone, Debug)]
1162pub(crate) enum ExclusivePipeline {
1163    None,
1164    Render(Weak<RenderPipeline>),
1165    Compute(Weak<ComputePipeline>),
1166}
1167
1168impl From<&Arc<RenderPipeline>> for ExclusivePipeline {
1169    fn from(pipeline: &Arc<RenderPipeline>) -> Self {
1170        Self::Render(Arc::downgrade(pipeline))
1171    }
1172}
1173
1174impl From<&Arc<ComputePipeline>> for ExclusivePipeline {
1175    fn from(pipeline: &Arc<ComputePipeline>) -> Self {
1176        Self::Compute(Arc::downgrade(pipeline))
1177    }
1178}
1179
1180impl fmt::Display for ExclusivePipeline {
1181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1182        match self {
1183            ExclusivePipeline::None => f.write_str("None"),
1184            ExclusivePipeline::Render(p) => {
1185                if let Some(p) = p.upgrade() {
1186                    p.error_ident().fmt(f)
1187                } else {
1188                    f.write_str("RenderPipeline")
1189                }
1190            }
1191            ExclusivePipeline::Compute(p) => {
1192                if let Some(p) = p.upgrade() {
1193                    p.error_ident().fmt(f)
1194                } else {
1195                    f.write_str("ComputePipeline")
1196                }
1197            }
1198        }
1199    }
1200}
1201
1202#[derive(Debug)]
1203pub enum RawBindGroupLayout {
1204    Owning(ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>),
1205    /// The empty BGL was created by the device and will be destroyed by the device.
1206    RefDeviceEmptyBGL,
1207}
1208
1209#[derive(Debug)]
1210pub(crate) struct BindGroupLayoutState {
1211    pub(crate) raw: RawBindGroupLayout,
1212    /// It is very important that we know if the bind group comes from the BGL pool.
1213    ///
1214    /// If it does, then we need to remove it from the pool when we drop it.
1215    ///
1216    /// We cannot unconditionally remove from the pool, as BGLs that don't come from the pool
1217    /// (derived BGLs) must not be removed.
1218    pub(crate) origin: bgl::Origin,
1219    pub(crate) binding_count_validator: BindingTypeMaxCountValidator,
1220}
1221
1222/// Bind group layout.
1223#[derive(Debug)]
1224pub struct BindGroupLayout {
1225    pub(crate) state: ResourceState<BindGroupLayoutState>,
1226    pub(crate) device: Arc<Device>,
1227    pub(crate) entries: bgl::EntryMap,
1228    pub(crate) exclusive_pipeline: OnceCell<ExclusivePipeline>,
1229    /// The `label` from the descriptor used to create the resource.
1230    pub(crate) label: String,
1231}
1232
1233impl Drop for BindGroupLayout {
1234    #[allow(trivial_casts)]
1235    fn drop(&mut self) {
1236        profiling::scope!("BindGroupLayout::drop");
1237        api_log!("BindGroupLayout::drop {:?}", self as *const _);
1238        #[cfg(feature = "trace")]
1239        {
1240            let mut t = self.device.trace.lock();
1241            if let Some(t) = t.as_mut() {
1242                use crate::device::trace;
1243
1244                // SAFETY: All bind group layouts are constructed in Arc => are heap allocated
1245                t.add(trace::Action::DropBindGroupLayout(unsafe {
1246                    trace::to_trace(self)
1247                }));
1248            }
1249        }
1250        resource_log!("Destroy raw {}", self.error_ident());
1251        let ResourceState::Valid(state) = &mut self.state else {
1252            return;
1253        };
1254        if matches!(state.origin, bgl::Origin::Pool) {
1255            self.device.bgl_pool.remove(&self.entries);
1256        }
1257        match state.raw {
1258            RawBindGroupLayout::Owning(ref mut raw) => {
1259                // SAFETY: We are in the Drop impl and we don't use state.raw anymore after this point.
1260                let raw = unsafe { ManuallyDrop::take(raw) };
1261                unsafe {
1262                    self.device.raw().destroy_bind_group_layout(raw);
1263                }
1264            }
1265            RawBindGroupLayout::RefDeviceEmptyBGL => {}
1266        }
1267    }
1268}
1269
1270crate::impl_resource_type!(BindGroupLayout);
1271crate::impl_labeled!(BindGroupLayout);
1272crate::impl_parent_device!(BindGroupLayout);
1273crate::impl_storage_item!(BindGroupLayout);
1274
1275impl BindGroupLayout {
1276    pub(crate) fn try_raw(&self) -> Result<&dyn hal::DynBindGroupLayout, InvalidResourceError> {
1277        let ResourceState::Valid(state) = &self.state else {
1278            return Err(InvalidResourceError(self.error_ident()));
1279        };
1280        match &state.raw {
1281            RawBindGroupLayout::Owning(raw) => Ok(raw.as_ref()),
1282            RawBindGroupLayout::RefDeviceEmptyBGL => Ok(self.device.empty_bgl.as_ref()),
1283        }
1284    }
1285
1286    pub(crate) fn state(&self) -> Result<&BindGroupLayoutState, InvalidResourceError> {
1287        let ResourceState::Valid(state) = &self.state else {
1288            return Err(InvalidResourceError(self.error_ident()));
1289        };
1290        Ok(state)
1291    }
1292
1293    pub(crate) fn check_is_valid(self: &Arc<Self>) -> Result<(), InvalidResourceError> {
1294        let ResourceState::Valid(_) = &self.state else {
1295            return Err(InvalidResourceError(self.error_ident()));
1296        };
1297        Ok(())
1298    }
1299
1300    fn empty(device: &Arc<Device>, exclusive_pipeline: ExclusivePipeline) -> Arc<Self> {
1301        Arc::new(Self {
1302            state: ResourceState::Valid(BindGroupLayoutState {
1303                raw: RawBindGroupLayout::RefDeviceEmptyBGL,
1304                origin: bgl::Origin::Derived,
1305                binding_count_validator: BindingTypeMaxCountValidator::default(),
1306            }),
1307            device: device.clone(),
1308            entries: bgl::EntryMap::default(),
1309            exclusive_pipeline: OnceCell::from(exclusive_pipeline),
1310            label: String::new(),
1311        })
1312    }
1313
1314    pub fn invalid(device: &Arc<Device>, label: String) -> Arc<Self> {
1315        Arc::new(Self {
1316            state: ResourceState::Invalid,
1317            device: device.clone(),
1318            entries: bgl::EntryMap::default(),
1319            exclusive_pipeline: OnceCell::from(ExclusivePipeline::None),
1320            label,
1321        })
1322    }
1323}
1324
1325#[derive(Clone, Debug, Error)]
1326#[non_exhaustive]
1327pub enum CreatePipelineLayoutError {
1328    #[error(transparent)]
1329    Device(#[from] DeviceError),
1330    #[error(
1331        "Immediate data has range bound {size} which is not aligned to IMMEDIATE_DATA_ALIGNMENT ({})",
1332        wgt::IMMEDIATE_DATA_ALIGNMENT
1333    )]
1334    MisalignedImmediateSize { size: u32 },
1335    #[error(transparent)]
1336    MissingFeatures(#[from] MissingFeatures),
1337    #[error(
1338        "Immediate data has size {size} which exceeds device immediate data size limit 0..{max}"
1339    )]
1340    ImmediateRangeTooLarge { size: u32, max: u32 },
1341    #[error(transparent)]
1342    TooManyBindings(BindingTypeMaxCountError),
1343    #[error("Bind group layout count {actual} exceeds device bind group limit {max}")]
1344    TooManyGroups { actual: usize, max: usize },
1345    #[error(transparent)]
1346    InvalidResource(#[from] InvalidResourceError),
1347    #[error("Bind group layout at index {index} has an exclusive pipeline: {pipeline}")]
1348    BglHasExclusivePipeline { index: usize, pipeline: String },
1349}
1350
1351impl WebGpuError for CreatePipelineLayoutError {
1352    fn webgpu_error_type(&self) -> ErrorType {
1353        match self {
1354            Self::Device(e) => e.webgpu_error_type(),
1355            Self::MissingFeatures(e) => e.webgpu_error_type(),
1356            Self::InvalidResource(e) => e.webgpu_error_type(),
1357            Self::TooManyBindings(e) => e.webgpu_error_type(),
1358            Self::MisalignedImmediateSize { .. }
1359            | Self::ImmediateRangeTooLarge { .. }
1360            | Self::TooManyGroups { .. }
1361            | Self::BglHasExclusivePipeline { .. } => ErrorType::Validation,
1362        }
1363    }
1364}
1365
1366#[derive(Clone, Debug, Error)]
1367#[non_exhaustive]
1368pub enum ImmediateUploadError {
1369    #[error(
1370        "Provided immediate data start offset {start_offset} overruns the range with a size of {immediate_size}"
1371    )]
1372    StartOffsetOverrun {
1373        start_offset: u32,
1374        immediate_size: u32,
1375    },
1376    #[error(
1377        "Provided immediate data start offset {0} does not respect \
1378        `IMMEDIATE_DATA_ALIGNMENT` ({ida})",
1379        ida = wgt::IMMEDIATE_DATA_ALIGNMENT
1380    )]
1381    StartOffsetUnaligned(u32),
1382    #[error(
1383        "Provided immediate data byte size {0} does not respect \
1384        `IMMEDIATE_DATA_ALIGNMENT` ({ida})",
1385        ida = wgt::IMMEDIATE_DATA_ALIGNMENT
1386    )]
1387    SizeUnaligned(usize),
1388    #[error(
1389        "Provided immediate data start offset {} + size {} overruns `max_immediate_size` {}",
1390        start_offset,
1391        size_bytes,
1392        limit
1393    )]
1394    EndOffsetBeyondLimit {
1395        start_offset: u32,
1396        size_bytes: usize,
1397        limit: u32,
1398    },
1399}
1400
1401impl WebGpuError for ImmediateUploadError {
1402    fn webgpu_error_type(&self) -> ErrorType {
1403        ErrorType::Validation
1404    }
1405}
1406
1407/// Describes a pipeline layout.
1408///
1409/// A `PipelineLayoutDescriptor` can be used to create a pipeline layout.
1410#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1411#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1412#[cfg_attr(feature = "serde", serde(bound = "BGL: Serialize"))]
1413/// cbindgen:ignore
1414pub struct PipelineLayoutDescriptor<'a, BGL = Arc<BindGroupLayout>>
1415where
1416    [Option<BGL>]: ToOwned,
1417    <[Option<BGL>] as ToOwned>::Owned: fmt::Debug,
1418{
1419    /// Debug label of the pipeline layout.
1420    ///
1421    /// This will show up in graphics debuggers for easy identification.
1422    pub label: Label<'a>,
1423    /// Bind groups that this pipeline uses. The first entry will provide all the bindings for
1424    /// "set = 0", second entry will provide all the bindings for "set = 1" etc.
1425    #[cfg_attr(
1426        feature = "serde",
1427        serde(bound(deserialize = "<[Option<BGL>] as ToOwned>::Owned: Deserialize<'de>"))
1428    )]
1429    pub bind_group_layouts: Cow<'a, [Option<BGL>]>,
1430    /// The number of bytes of immediate data that are allocated for use
1431    /// in the shader. The `var<immediate>`s in the shader attached to
1432    /// this pipeline must be equal or smaller than this size.
1433    ///
1434    /// If this value is non-zero, [`wgt::Features::IMMEDIATES`] must be enabled.
1435    pub immediate_size: u32,
1436}
1437
1438#[derive(Debug)]
1439pub struct PipelineLayout {
1440    pub(crate) raw: ResourceState<Box<dyn hal::DynPipelineLayout>>,
1441    pub(crate) device: Arc<Device>,
1442    /// The `label` from the descriptor used to create the resource.
1443    pub(crate) label: String,
1444    pub(crate) bind_group_layouts: ArrayVec<Option<Arc<BindGroupLayout>>, { hal::MAX_BIND_GROUPS }>,
1445    pub(crate) immediate_size: u32,
1446    pub(crate) buffers_and_acceleration_structures_in_vertex_stage: u32,
1447}
1448
1449impl Drop for PipelineLayout {
1450    #[allow(trivial_casts)]
1451    fn drop(&mut self) {
1452        profiling::scope!("PipelineLayout::drop");
1453        api_log!("PipelineLayout::drop {:?}", self as *const _);
1454        resource_log!("Destroy raw {}", self.error_ident());
1455        if let ResourceState::Valid(raw) = core::mem::replace(&mut self.raw, ResourceState::Invalid)
1456        {
1457            unsafe {
1458                self.device.raw().destroy_pipeline_layout(raw);
1459            }
1460        }
1461        #[cfg(feature = "trace")]
1462        {
1463            if let Some(t) = self.device.trace.lock().as_mut() {
1464                t.add(crate::device::trace::Action::DropPipelineLayout(unsafe {
1465                    crate::device::trace::to_trace(self)
1466                }));
1467            }
1468        }
1469    }
1470}
1471
1472impl PipelineLayout {
1473    pub(crate) fn raw(&self) -> Result<&dyn hal::DynPipelineLayout, InvalidResourceError> {
1474        self.raw
1475            .as_ref()
1476            .valid()
1477            .map(|r| r.as_ref())
1478            .ok_or_else(|| InvalidResourceError(self.error_ident()))
1479    }
1480
1481    pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
1482        self.raw
1483            .as_ref()
1484            .valid()
1485            .map(|_| ())
1486            .ok_or_else(|| InvalidResourceError(self.error_ident()))
1487    }
1488
1489    pub(crate) fn get_bind_group_layout(
1490        self: &Arc<Self>,
1491        index: u32,
1492        exclusive_pipeline_for_empty_bgl: ExclusivePipeline,
1493    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
1494        let max_bind_groups = self.device.limits.max_bind_groups;
1495        if index >= max_bind_groups {
1496            return Err(GetBindGroupLayoutError::IndexOutOfRange {
1497                index,
1498                max: max_bind_groups,
1499            });
1500        }
1501        Ok(self
1502            .bind_group_layouts
1503            .get(index as usize)
1504            .cloned()
1505            .flatten()
1506            .unwrap_or_else(|| {
1507                BindGroupLayout::empty(&self.device, exclusive_pipeline_for_empty_bgl)
1508            }))
1509    }
1510
1511    pub(crate) fn get_bgl_entry(
1512        &self,
1513        group: u32,
1514        binding: u32,
1515    ) -> Option<&wgt::BindGroupLayoutEntry> {
1516        let bgl = self.bind_group_layouts.get(group as usize)?;
1517        let bgl = bgl.as_ref()?;
1518        bgl.entries.get(binding)
1519    }
1520
1521    pub(crate) fn invalid(device: Arc<Device>, label: String) -> Arc<Self> {
1522        Arc::new(Self {
1523            raw: ResourceState::Invalid,
1524            device,
1525            label,
1526            bind_group_layouts: ArrayVec::new(),
1527            immediate_size: 0,
1528            buffers_and_acceleration_structures_in_vertex_stage: 0,
1529        })
1530    }
1531}
1532
1533crate::impl_resource_type!(PipelineLayout);
1534crate::impl_labeled!(PipelineLayout);
1535crate::impl_parent_device!(PipelineLayout);
1536crate::impl_storage_item!(PipelineLayout);
1537
1538#[repr(C)]
1539#[derive(Clone, Debug, Hash, Eq, PartialEq)]
1540#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1541pub struct BufferBinding<B = Arc<Buffer>> {
1542    pub buffer: B,
1543    pub offset: wgt::BufferAddress,
1544
1545    /// Size of the binding. If `None`, the binding spans from `offset` to the
1546    /// end of the buffer.
1547    ///
1548    /// We use `BufferAddress` to allow a size of zero on this `wgpu_core` type,
1549    /// because JavaScript bindings cannot readily express `Option<NonZeroU64>`.
1550    /// The `wgpu` API uses `Option<BufferSize>` (i.e. `NonZeroU64`) for this
1551    /// field.
1552    pub size: Option<wgt::BufferAddress>,
1553}
1554
1555// Note: Duplicated in `wgpu-rs` as `BindingResource`
1556// They're different enough that it doesn't make sense to share a common type
1557#[derive(Debug, Clone)]
1558#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1559pub enum BindingResource<
1560    'a,
1561    B = Arc<Buffer>,
1562    S = Arc<Sampler>,
1563    TV = Arc<TextureView>,
1564    TLAS = Arc<Tlas>,
1565    ET = Arc<ExternalTexture>,
1566> where
1567    [BufferBinding<B>]: ToOwned,
1568    [S]: ToOwned,
1569    [TV]: ToOwned,
1570    [TLAS]: ToOwned,
1571    <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
1572    <[S] as ToOwned>::Owned: fmt::Debug,
1573    <[TV] as ToOwned>::Owned: fmt::Debug,
1574    <[TLAS] as ToOwned>::Owned: fmt::Debug,
1575{
1576    Buffer(BufferBinding<B>),
1577    #[cfg_attr(
1578        feature = "serde",
1579        serde(bound(deserialize = "<[BufferBinding<B>] as ToOwned>::Owned: Deserialize<'de>"))
1580    )]
1581    BufferArray(Cow<'a, [BufferBinding<B>]>),
1582    Sampler(S),
1583    #[cfg_attr(
1584        feature = "serde",
1585        serde(bound(deserialize = "<[S] as ToOwned>::Owned: Deserialize<'de>"))
1586    )]
1587    SamplerArray(Cow<'a, [S]>),
1588    TextureView(TV),
1589    #[cfg_attr(
1590        feature = "serde",
1591        serde(bound(deserialize = "<[TV] as ToOwned>::Owned: Deserialize<'de>"))
1592    )]
1593    TextureViewArray(Cow<'a, [TV]>),
1594    AccelerationStructure(TLAS),
1595    #[cfg_attr(
1596        feature = "serde",
1597        serde(bound(deserialize = "<[TLAS] as ToOwned>::Owned: Deserialize<'de>"))
1598    )]
1599    AccelerationStructureArray(Cow<'a, [TLAS]>),
1600    ExternalTexture(ET),
1601}
1602
1603#[derive(Clone, Debug, Error)]
1604#[non_exhaustive]
1605pub enum BindError {
1606    #[error(
1607        "Dynamic offsets not expected with null bind group at index {group}. However {actual} dynamic offset{s1} were provided.",
1608        s1 = if *.actual >= 2 { "s" } else { "" },
1609    )]
1610    DynamicOffsetCountNotZero { group: u32, actual: usize },
1611    #[error(
1612        "{bind_group} at index {group} expects {expected} dynamic offset{s0}, but {actual} dynamic offset{s1} {to_be} provided",
1613        s0 = if *.expected >= 2 { "s" } else { "" },
1614        s1 = if *.actual >= 2 { "s" } else { "" },
1615        to_be = if *.actual == 1 { "was" } else { "were" },
1616    )]
1617    MismatchedDynamicOffsetCount {
1618        bind_group: ResourceErrorIdent,
1619        group: u32,
1620        actual: usize,
1621        expected: usize,
1622    },
1623    #[error(
1624        "Dynamic binding index {idx} (targeting {bind_group} {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}"
1625    )]
1626    UnalignedDynamicBinding {
1627        bind_group: ResourceErrorIdent,
1628        idx: usize,
1629        group: u32,
1630        binding: u32,
1631        offset: u32,
1632        alignment: u32,
1633        limit_name: &'static str,
1634    },
1635    #[error(
1636        "Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to {bind_group} {group} -> binding {binding}. \
1637         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",
1638    )]
1639    DynamicBindingOutOfBounds {
1640        bind_group: ResourceErrorIdent,
1641        idx: usize,
1642        group: u32,
1643        binding: u32,
1644        offset: u32,
1645        buffer_size: wgt::BufferAddress,
1646        binding_range: Range<wgt::BufferAddress>,
1647        maximum_dynamic_offset: wgt::BufferAddress,
1648    },
1649}
1650
1651impl WebGpuError for BindError {
1652    fn webgpu_error_type(&self) -> ErrorType {
1653        ErrorType::Validation
1654    }
1655}
1656
1657#[derive(Debug)]
1658pub struct BindGroupDynamicBindingData {
1659    /// The index of the binding.
1660    ///
1661    /// Used for more descriptive errors.
1662    pub(crate) binding_idx: u32,
1663    /// The size of the buffer.
1664    ///
1665    /// Used for more descriptive errors.
1666    pub(crate) buffer_size: wgt::BufferAddress,
1667    /// The range that the binding covers.
1668    ///
1669    /// Used for more descriptive errors.
1670    pub(crate) binding_range: Range<wgt::BufferAddress>,
1671    /// The maximum value the dynamic offset can have before running off the end of the buffer.
1672    pub(crate) maximum_dynamic_offset: wgt::BufferAddress,
1673    /// The binding type.
1674    pub(crate) binding_type: wgt::BufferBindingType,
1675}
1676
1677pub(crate) fn buffer_binding_type_alignment(
1678    limits: &wgt::Limits,
1679    binding_type: wgt::BufferBindingType,
1680) -> (u32, &'static str) {
1681    match binding_type {
1682        wgt::BufferBindingType::Uniform => (
1683            limits.min_uniform_buffer_offset_alignment,
1684            "min_uniform_buffer_offset_alignment",
1685        ),
1686        wgt::BufferBindingType::Storage { .. } => (
1687            limits.min_storage_buffer_offset_alignment,
1688            "min_storage_buffer_offset_alignment",
1689        ),
1690    }
1691}
1692
1693pub(crate) fn buffer_binding_type_bounds_check_alignment(
1694    alignments: &hal::Alignments,
1695    binding_type: wgt::BufferBindingType,
1696) -> wgt::BufferAddress {
1697    match binding_type {
1698        wgt::BufferBindingType::Uniform => alignments.uniform_bounds_check_alignment.get(),
1699        wgt::BufferBindingType::Storage { .. } => wgt::COPY_BUFFER_ALIGNMENT,
1700    }
1701}
1702
1703#[derive(Debug)]
1704pub(crate) struct BindGroupLateBufferBindingInfo {
1705    /// The normal binding index in the bind group.
1706    pub binding_index: u32,
1707    /// The size that exists at bind time.
1708    pub size: wgt::BufferSize,
1709}
1710
1711#[derive(Debug)]
1712pub(crate) struct BindGroupState {
1713    pub(crate) raw: Snatchable<Box<dyn hal::DynBindGroup>>,
1714}
1715
1716#[derive(Debug)]
1717pub struct BindGroup {
1718    pub(crate) state: ResourceState<BindGroupState>,
1719    pub(crate) device: Arc<Device>,
1720    pub(crate) layout: Arc<BindGroupLayout>,
1721    /// The `label` from the descriptor used to create the resource.
1722    pub(crate) label: String,
1723    pub(crate) tracking_data: TrackingData,
1724    pub(crate) used: BindGroupStates,
1725    pub(crate) buffer_init_actions: Vec<BufferInitTrackerAction>,
1726    pub(crate) texture_init_actions: Vec<TextureInitTrackerAction>,
1727    /// INVARIANT: Sorted by binding index order.
1728    pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
1729    /// Actual binding sizes for buffers that don't have `min_binding_size`
1730    /// specified in BGL. Listed in the order of iteration of `BGL.entries`.
1731    pub(crate) late_buffer_binding_infos: Vec<BindGroupLateBufferBindingInfo>,
1732}
1733
1734impl Drop for BindGroup {
1735    #[allow(trivial_casts)]
1736    fn drop(&mut self) {
1737        profiling::scope!("BindGroup::drop");
1738        api_log!("BindGroup::drop {:?}", self as *const _);
1739        #[cfg(feature = "trace")]
1740        if let Some(t) = self.device.trace.lock().as_mut() {
1741            use crate::device::trace::{to_trace, Action};
1742            t.add(Action::DropBindGroup(unsafe { to_trace(self) }));
1743        }
1744        let ResourceState::Valid(state) = &mut self.state else {
1745            return;
1746        };
1747        if let Some(raw) = state.raw.take() {
1748            resource_log!("Destroy raw {}", self.error_ident());
1749            unsafe {
1750                self.device.raw().destroy_bind_group(raw);
1751            }
1752        }
1753    }
1754}
1755
1756impl BindGroup {
1757    pub(crate) fn try_raw<'a>(
1758        &'a self,
1759        guard: &'a SnatchGuard,
1760    ) -> Result<&'a dyn hal::DynBindGroup, InvalidOrDestroyedResourceError> {
1761        for buffer in self.used.buffers.used_resources() {
1762            buffer.try_raw(guard)?;
1763        }
1764        for texture in self.used.views.used_textures() {
1765            texture.try_raw(guard)?;
1766        }
1767
1768        self.state()?
1769            .raw
1770            .get(guard)
1771            .map(|raw| raw.as_ref())
1772            .ok_or_else(|| DestroyedResourceError(self.error_ident()).into())
1773    }
1774
1775    pub(crate) fn state(&self) -> Result<&BindGroupState, InvalidResourceError> {
1776        let ResourceState::Valid(state) = &self.state else {
1777            return Err(InvalidResourceError(self.error_ident()));
1778        };
1779        Ok(state)
1780    }
1781
1782    pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
1783        self.state().map(|_| ())
1784    }
1785
1786    pub(crate) fn invalid(
1787        device: Arc<Device>,
1788        label: String,
1789        layout: Arc<BindGroupLayout>,
1790    ) -> Arc<Self> {
1791        Arc::new(Self {
1792            state: ResourceState::Invalid,
1793            layout,
1794            label,
1795            tracking_data: TrackingData::new(device.tracker_indices.bind_groups.clone()),
1796            used: BindGroupStates::new(),
1797            buffer_init_actions: Vec::new(),
1798            texture_init_actions: Vec::new(),
1799            dynamic_binding_info: Vec::new(),
1800            late_buffer_binding_infos: Vec::new(),
1801            device,
1802        })
1803    }
1804
1805    pub(crate) fn validate_dynamic_bindings(
1806        &self,
1807        bind_group_index: u32,
1808        offsets: &[wgt::DynamicOffset],
1809    ) -> Result<(), BindError> {
1810        if self.dynamic_binding_info.len() != offsets.len() {
1811            return Err(BindError::MismatchedDynamicOffsetCount {
1812                bind_group: self.error_ident(),
1813                group: bind_group_index,
1814                expected: self.dynamic_binding_info.len(),
1815                actual: offsets.len(),
1816            });
1817        }
1818
1819        for (idx, (info, &offset)) in self
1820            .dynamic_binding_info
1821            .iter()
1822            .zip(offsets.iter())
1823            .enumerate()
1824        {
1825            let (alignment, limit_name) =
1826                buffer_binding_type_alignment(&self.device.limits, info.binding_type);
1827            if !(offset as wgt::BufferAddress).is_multiple_of(alignment as u64) {
1828                return Err(BindError::UnalignedDynamicBinding {
1829                    bind_group: self.error_ident(),
1830                    group: bind_group_index,
1831                    binding: info.binding_idx,
1832                    idx,
1833                    offset,
1834                    alignment,
1835                    limit_name,
1836                });
1837            }
1838
1839            if offset as wgt::BufferAddress > info.maximum_dynamic_offset {
1840                return Err(BindError::DynamicBindingOutOfBounds {
1841                    bind_group: self.error_ident(),
1842                    group: bind_group_index,
1843                    binding: info.binding_idx,
1844                    idx,
1845                    offset,
1846                    buffer_size: info.buffer_size,
1847                    binding_range: info.binding_range.clone(),
1848                    maximum_dynamic_offset: info.maximum_dynamic_offset,
1849                });
1850            }
1851        }
1852
1853        Ok(())
1854    }
1855}
1856
1857crate::impl_resource_type!(BindGroup);
1858crate::impl_labeled!(BindGroup);
1859crate::impl_parent_device!(BindGroup);
1860crate::impl_storage_item!(BindGroup);
1861crate::impl_trackable!(BindGroup);
1862
1863#[derive(Clone, Debug, Error)]
1864#[non_exhaustive]
1865pub enum GetBindGroupLayoutError {
1866    #[error("Bind group layout index {index} is greater than the device's configured `max_bind_groups` limit {max}")]
1867    IndexOutOfRange { index: u32, max: u32 },
1868    #[error(transparent)]
1869    InvalidResource(#[from] InvalidResourceError),
1870}
1871
1872impl WebGpuError for GetBindGroupLayoutError {
1873    fn webgpu_error_type(&self) -> ErrorType {
1874        match self {
1875            Self::IndexOutOfRange { .. } => ErrorType::Validation,
1876            Self::InvalidResource(e) => e.webgpu_error_type(),
1877        }
1878    }
1879}
1880
1881#[derive(Clone, Debug, Error, Eq, PartialEq)]
1882#[error(
1883    "In bind group index {group_index}, the buffer bound at binding index {binding_index} \
1884     is bound with size {bound_size} where the shader expects {shader_size}."
1885)]
1886pub struct LateMinBufferBindingSizeMismatch {
1887    pub group_index: u32,
1888    pub binding_index: u32,
1889    pub shader_size: wgt::BufferAddress,
1890    pub bound_size: wgt::BufferAddress,
1891}