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