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