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