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