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