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