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