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