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