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