1use alloc::{
2 borrow::{Cow, ToOwned},
3 boxed::Box,
4 string::String,
5 sync::{Arc, Weak},
6 vec::Vec,
7};
8use core::{fmt, mem::ManuallyDrop, ops::Range};
9
10use arrayvec::ArrayVec;
11use thiserror::Error;
12
13#[cfg(feature = "serde")]
14use serde::Deserialize;
15#[cfg(feature = "serde")]
16use serde::Serialize;
17
18use wgt::error::{ErrorType, WebGpuError};
19
20use crate::{
21 device::{bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures},
22 id::{BindGroupLayoutId, BufferId, ExternalTextureId, SamplerId, TextureViewId, TlasId},
23 init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
24 pipeline::{ComputePipeline, RenderPipeline},
25 resource::{
26 Buffer, DestroyedResourceError, ExternalTexture, InvalidResourceError, Labeled,
27 MissingBufferUsageError, MissingTextureUsageError, RawResourceAccess, ResourceErrorIdent,
28 Sampler, TextureView, Tlas, TrackingData,
29 },
30 resource_log,
31 snatch::{SnatchGuard, Snatchable},
32 track::{BindGroupStates, ResourceUsageCompatibilityError},
33 Label,
34};
35
36#[derive(Clone, Debug, Error)]
37#[non_exhaustive]
38pub enum BindGroupLayoutEntryError {
39 #[error("Cube dimension is not expected for texture storage")]
40 StorageTextureCube,
41 #[error("Atomic storage textures are not allowed by baseline webgpu, they require the native only feature TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES")]
42 StorageTextureAtomic,
43 #[error("Arrays of bindings unsupported for this type of binding")]
44 ArrayUnsupported,
45 #[error("Multisampled binding with sample type `TextureSampleType::Float` must have filterable set to false.")]
46 SampleTypeFloatFilterableBindingMultisampled,
47 #[error("Multisampled texture binding view dimension must be 2d, got {0:?}")]
48 Non2DMultisampled(wgt::TextureViewDimension),
49 #[error(transparent)]
50 MissingFeatures(#[from] MissingFeatures),
51 #[error(transparent)]
52 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
53}
54
55#[derive(Clone, Debug, Error)]
56#[non_exhaustive]
57pub enum CreateBindGroupLayoutError {
58 #[error(transparent)]
59 Device(#[from] DeviceError),
60 #[error("Conflicting binding at index {0}")]
61 ConflictBinding(u32),
62 #[error("Binding {binding} entry is invalid")]
63 Entry {
64 binding: u32,
65 #[source]
66 error: BindGroupLayoutEntryError,
67 },
68 #[error(transparent)]
69 TooManyBindings(BindingTypeMaxCountError),
70 #[error("Bind groups may not contain both a binding array and a dynamically offset buffer")]
71 ContainsBothBindingArrayAndDynamicOffsetArray,
72 #[error("Bind groups may not contain both a binding array and a uniform buffer")]
73 ContainsBothBindingArrayAndUniformBuffer,
74 #[error("Binding index {binding} is greater than the maximum number {maximum}")]
75 InvalidBindingIndex { binding: u32, maximum: u32 },
76 #[error("Invalid visibility {0:?}")]
77 InvalidVisibility(wgt::ShaderStages),
78}
79
80impl WebGpuError for CreateBindGroupLayoutError {
81 fn webgpu_error_type(&self) -> ErrorType {
82 match self {
83 Self::Device(e) => e.webgpu_error_type(),
84
85 Self::ConflictBinding(_)
86 | Self::Entry { .. }
87 | Self::TooManyBindings(_)
88 | Self::InvalidBindingIndex { .. }
89 | Self::InvalidVisibility(_)
90 | Self::ContainsBothBindingArrayAndDynamicOffsetArray
91 | Self::ContainsBothBindingArrayAndUniformBuffer => ErrorType::Validation,
92 }
93 }
94}
95
96#[derive(Clone, Debug, Error)]
97#[non_exhaustive]
98pub enum BindingError {
99 #[error(transparent)]
100 DestroyedResource(#[from] DestroyedResourceError),
101 #[error("Buffer {buffer}: Binding with size {binding_size} at offset {offset} would overflow buffer size of {buffer_size}")]
102 BindingRangeTooLarge {
103 buffer: ResourceErrorIdent,
104 offset: wgt::BufferAddress,
105 binding_size: u64,
106 buffer_size: u64,
107 },
108 #[error("Buffer {buffer}: Binding offset {offset} is greater than buffer size {buffer_size}")]
109 BindingOffsetTooLarge {
110 buffer: ResourceErrorIdent,
111 offset: wgt::BufferAddress,
112 buffer_size: u64,
113 },
114}
115
116impl WebGpuError for BindingError {
117 fn webgpu_error_type(&self) -> ErrorType {
118 match self {
119 Self::DestroyedResource(e) => e.webgpu_error_type(),
120 Self::BindingRangeTooLarge { .. } | Self::BindingOffsetTooLarge { .. } => {
121 ErrorType::Validation
122 }
123 }
124 }
125}
126
127#[derive(Clone, Debug, Error)]
130#[non_exhaustive]
131pub enum CreateBindGroupError {
132 #[error(transparent)]
133 Device(#[from] DeviceError),
134 #[error(transparent)]
135 DestroyedResource(#[from] DestroyedResourceError),
136 #[error(transparent)]
137 BindingError(#[from] BindingError),
138 #[error(
139 "Binding count declared with at most {expected} items, but {actual} items were provided"
140 )]
141 BindingArrayPartialLengthMismatch { actual: usize, expected: usize },
142 #[error(
143 "Binding count declared with exactly {expected} items, but {actual} items were provided"
144 )]
145 BindingArrayLengthMismatch { actual: usize, expected: usize },
146 #[error("Array binding provided zero elements")]
147 BindingArrayZeroLength,
148 #[error("Binding size {actual} of {buffer} is less than minimum {min}")]
149 BindingSizeTooSmall {
150 buffer: ResourceErrorIdent,
151 actual: u64,
152 min: u64,
153 },
154 #[error("{0} binding size is zero")]
155 BindingZeroSize(ResourceErrorIdent),
156 #[error("Number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
157 BindingsNumMismatch { actual: usize, expected: usize },
158 #[error("Binding {0} is used at least twice in the descriptor")]
159 DuplicateBinding(u32),
160 #[error("Unable to find a corresponding declaration for the given binding {0}")]
161 MissingBindingDeclaration(u32),
162 #[error(transparent)]
163 MissingBufferUsage(#[from] MissingBufferUsageError),
164 #[error(transparent)]
165 MissingTextureUsage(#[from] MissingTextureUsageError),
166 #[error("Binding declared as a single item, but bind group is using it as an array")]
167 SingleBindingExpected,
168 #[error("Effective buffer binding size {size} for storage buffers is expected to align to {alignment}, but size is {size}")]
169 UnalignedEffectiveBufferBindingSizeForStorage { alignment: u32, size: u64 },
170 #[error("Buffer offset {0} does not respect device's requested `{1}` limit {2}")]
171 UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32),
172 #[error(
173 "Buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
174 )]
175 BufferRangeTooLarge {
176 binding: u32,
177 given: u32,
178 limit: u32,
179 },
180 #[error("Binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
181 WrongBindingType {
182 binding: u32,
184 actual: wgt::BindingType,
186 expected: &'static str,
188 },
189 #[error("Texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}")]
190 InvalidTextureMultisample {
191 binding: u32,
192 layout_multisampled: bool,
193 view_samples: u32,
194 },
195 #[error(
196 "Texture binding {} expects sample type {:?}, but was given a view with format {:?} (sample type {:?})",
197 binding,
198 layout_sample_type,
199 view_format,
200 view_sample_type
201 )]
202 InvalidTextureSampleType {
203 binding: u32,
204 layout_sample_type: wgt::TextureSampleType,
205 view_format: wgt::TextureFormat,
206 view_sample_type: wgt::TextureSampleType,
207 },
208 #[error("Texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}")]
209 InvalidTextureDimension {
210 binding: u32,
211 layout_dimension: wgt::TextureViewDimension,
212 view_dimension: wgt::TextureViewDimension,
213 },
214 #[error("Storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}")]
215 InvalidStorageTextureFormat {
216 binding: u32,
217 layout_format: wgt::TextureFormat,
218 view_format: wgt::TextureFormat,
219 },
220 #[error("Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
221 InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },
222 #[error("External texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
223 InvalidExternalTextureMipLevelCount { binding: u32, mip_level_count: u32 },
224 #[error("External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = {format:?} at binding {binding}")]
225 InvalidExternalTextureFormat {
226 binding: u32,
227 format: wgt::TextureFormat,
228 },
229 #[error("Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}")]
230 WrongSamplerComparison {
231 binding: u32,
232 layout_cmp: bool,
233 sampler_cmp: bool,
234 },
235 #[error("Sampler binding {binding} expects filtering = {layout_flt}, but given a sampler with filtering = {sampler_flt}")]
236 WrongSamplerFiltering {
237 binding: u32,
238 layout_flt: bool,
239 sampler_flt: bool,
240 },
241 #[error("TLAS binding {binding} is required to support vertex returns but is missing flag AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN")]
242 MissingTLASVertexReturn { binding: u32 },
243 #[error("Bound texture views can not have both depth and stencil aspects enabled")]
244 DepthStencilAspect,
245 #[error("The adapter does not support read access for storage textures of format {0:?}")]
246 StorageReadNotSupported(wgt::TextureFormat),
247 #[error("The adapter does not support atomics for storage textures of format {0:?}")]
248 StorageAtomicNotSupported(wgt::TextureFormat),
249 #[error("The adapter does not support write access for storage textures of format {0:?}")]
250 StorageWriteNotSupported(wgt::TextureFormat),
251 #[error("The adapter does not support read-write access for storage textures of format {0:?}")]
252 StorageReadWriteNotSupported(wgt::TextureFormat),
253 #[error(transparent)]
254 ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
255 #[error(transparent)]
256 InvalidResource(#[from] InvalidResourceError),
257}
258
259impl WebGpuError for CreateBindGroupError {
260 fn webgpu_error_type(&self) -> ErrorType {
261 let e: &dyn WebGpuError = match self {
262 Self::Device(e) => e,
263 Self::DestroyedResource(e) => e,
264 Self::BindingError(e) => e,
265 Self::MissingBufferUsage(e) => e,
266 Self::MissingTextureUsage(e) => e,
267 Self::ResourceUsageCompatibility(e) => e,
268 Self::InvalidResource(e) => e,
269 Self::BindingArrayPartialLengthMismatch { .. }
270 | Self::BindingArrayLengthMismatch { .. }
271 | Self::BindingArrayZeroLength
272 | Self::BindingSizeTooSmall { .. }
273 | Self::BindingsNumMismatch { .. }
274 | Self::BindingZeroSize(_)
275 | Self::DuplicateBinding(_)
276 | Self::MissingBindingDeclaration(_)
277 | Self::SingleBindingExpected
278 | Self::UnalignedEffectiveBufferBindingSizeForStorage { .. }
279 | Self::UnalignedBufferOffset(_, _, _)
280 | Self::BufferRangeTooLarge { .. }
281 | Self::WrongBindingType { .. }
282 | Self::InvalidTextureMultisample { .. }
283 | Self::InvalidTextureSampleType { .. }
284 | Self::InvalidTextureDimension { .. }
285 | Self::InvalidStorageTextureFormat { .. }
286 | Self::InvalidStorageTextureMipLevelCount { .. }
287 | Self::WrongSamplerComparison { .. }
288 | Self::WrongSamplerFiltering { .. }
289 | Self::DepthStencilAspect
290 | Self::StorageReadNotSupported(_)
291 | Self::StorageWriteNotSupported(_)
292 | Self::StorageReadWriteNotSupported(_)
293 | Self::StorageAtomicNotSupported(_)
294 | Self::MissingTLASVertexReturn { .. }
295 | Self::InvalidExternalTextureMipLevelCount { .. }
296 | Self::InvalidExternalTextureFormat { .. } => return ErrorType::Validation,
297 };
298 e.webgpu_error_type()
299 }
300}
301
302#[derive(Clone, Debug, Error)]
303pub enum BindingZone {
304 #[error("Stage {0:?}")]
305 Stage(wgt::ShaderStages),
306 #[error("Whole pipeline")]
307 Pipeline,
308}
309
310#[derive(Clone, Debug, Error)]
311#[error("Too many bindings of type {kind:?} in {zone}, limit is {limit}, count was {count}. Check the limit `{}` passed to `Adapter::request_device`", .kind.to_config_str())]
312pub struct BindingTypeMaxCountError {
313 pub kind: BindingTypeMaxCountErrorKind,
314 pub zone: BindingZone,
315 pub limit: u32,
316 pub count: u32,
317}
318
319impl WebGpuError for BindingTypeMaxCountError {
320 fn webgpu_error_type(&self) -> ErrorType {
321 ErrorType::Validation
322 }
323}
324
325#[derive(Clone, Debug)]
326pub enum BindingTypeMaxCountErrorKind {
327 DynamicUniformBuffers,
328 DynamicStorageBuffers,
329 SampledTextures,
330 Samplers,
331 StorageBuffers,
332 StorageTextures,
333 UniformBuffers,
334 BindingArrayElements,
335 BindingArraySamplerElements,
336 AccelerationStructures,
337}
338
339impl BindingTypeMaxCountErrorKind {
340 fn to_config_str(&self) -> &'static str {
341 match self {
342 BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {
343 "max_dynamic_uniform_buffers_per_pipeline_layout"
344 }
345 BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {
346 "max_dynamic_storage_buffers_per_pipeline_layout"
347 }
348 BindingTypeMaxCountErrorKind::SampledTextures => {
349 "max_sampled_textures_per_shader_stage"
350 }
351 BindingTypeMaxCountErrorKind::Samplers => "max_samplers_per_shader_stage",
352 BindingTypeMaxCountErrorKind::StorageBuffers => "max_storage_buffers_per_shader_stage",
353 BindingTypeMaxCountErrorKind::StorageTextures => {
354 "max_storage_textures_per_shader_stage"
355 }
356 BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
357 BindingTypeMaxCountErrorKind::BindingArrayElements => {
358 "max_binding_array_elements_per_shader_stage"
359 }
360 BindingTypeMaxCountErrorKind::BindingArraySamplerElements => {
361 "max_binding_array_sampler_elements_per_shader_stage"
362 }
363 BindingTypeMaxCountErrorKind::AccelerationStructures => {
364 "max_acceleration_structures_per_shader_stage"
365 }
366 }
367 }
368}
369
370#[derive(Debug, Default)]
371pub(crate) struct PerStageBindingTypeCounter {
372 vertex: u32,
373 fragment: u32,
374 compute: u32,
375}
376
377impl PerStageBindingTypeCounter {
378 pub(crate) fn add(&mut self, stage: wgt::ShaderStages, count: u32) {
379 if stage.contains(wgt::ShaderStages::VERTEX) {
380 self.vertex += count;
381 }
382 if stage.contains(wgt::ShaderStages::FRAGMENT) {
383 self.fragment += count;
384 }
385 if stage.contains(wgt::ShaderStages::COMPUTE) {
386 self.compute += count;
387 }
388 }
389
390 pub(crate) fn max(&self) -> (BindingZone, u32) {
391 let max_value = self.vertex.max(self.fragment.max(self.compute));
392 let mut stage = wgt::ShaderStages::NONE;
393 if max_value == self.vertex {
394 stage |= wgt::ShaderStages::VERTEX
395 }
396 if max_value == self.fragment {
397 stage |= wgt::ShaderStages::FRAGMENT
398 }
399 if max_value == self.compute {
400 stage |= wgt::ShaderStages::COMPUTE
401 }
402 (BindingZone::Stage(stage), max_value)
403 }
404
405 pub(crate) fn merge(&mut self, other: &Self) {
406 self.vertex = self.vertex.max(other.vertex);
407 self.fragment = self.fragment.max(other.fragment);
408 self.compute = self.compute.max(other.compute);
409 }
410
411 pub(crate) fn validate(
412 &self,
413 limit: u32,
414 kind: BindingTypeMaxCountErrorKind,
415 ) -> Result<(), BindingTypeMaxCountError> {
416 let (zone, count) = self.max();
417 if limit < count {
418 Err(BindingTypeMaxCountError {
419 kind,
420 zone,
421 limit,
422 count,
423 })
424 } else {
425 Ok(())
426 }
427 }
428}
429
430#[derive(Debug, Default)]
431pub(crate) struct BindingTypeMaxCountValidator {
432 dynamic_uniform_buffers: u32,
433 dynamic_storage_buffers: u32,
434 sampled_textures: PerStageBindingTypeCounter,
435 samplers: PerStageBindingTypeCounter,
436 storage_buffers: PerStageBindingTypeCounter,
437 storage_textures: PerStageBindingTypeCounter,
438 uniform_buffers: PerStageBindingTypeCounter,
439 acceleration_structures: PerStageBindingTypeCounter,
440 binding_array_elements: PerStageBindingTypeCounter,
441 binding_array_sampler_elements: PerStageBindingTypeCounter,
442 has_bindless_array: bool,
443}
444
445impl BindingTypeMaxCountValidator {
446 pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {
447 let count = binding.count.map_or(1, |count| count.get());
448
449 if binding.count.is_some() {
450 self.binding_array_elements.add(binding.visibility, count);
451 self.has_bindless_array = true;
452
453 if let wgt::BindingType::Sampler(_) = binding.ty {
454 self.binding_array_sampler_elements
455 .add(binding.visibility, count);
456 }
457 } else {
458 match binding.ty {
459 wgt::BindingType::Buffer {
460 ty: wgt::BufferBindingType::Uniform,
461 has_dynamic_offset,
462 ..
463 } => {
464 self.uniform_buffers.add(binding.visibility, count);
465 if has_dynamic_offset {
466 self.dynamic_uniform_buffers += count;
467 }
468 }
469 wgt::BindingType::Buffer {
470 ty: wgt::BufferBindingType::Storage { .. },
471 has_dynamic_offset,
472 ..
473 } => {
474 self.storage_buffers.add(binding.visibility, count);
475 if has_dynamic_offset {
476 self.dynamic_storage_buffers += count;
477 }
478 }
479 wgt::BindingType::Sampler { .. } => {
480 self.samplers.add(binding.visibility, count);
481 }
482 wgt::BindingType::Texture { .. } => {
483 self.sampled_textures.add(binding.visibility, count);
484 }
485 wgt::BindingType::StorageTexture { .. } => {
486 self.storage_textures.add(binding.visibility, count);
487 }
488 wgt::BindingType::AccelerationStructure { .. } => {
489 self.acceleration_structures.add(binding.visibility, count);
490 }
491 wgt::BindingType::ExternalTexture => {
492 self.sampled_textures.add(binding.visibility, count * 4);
501 self.samplers.add(binding.visibility, count);
502 self.uniform_buffers.add(binding.visibility, count);
503 }
504 }
505 }
506 }
507
508 pub(crate) fn merge(&mut self, other: &Self) {
509 self.dynamic_uniform_buffers += other.dynamic_uniform_buffers;
510 self.dynamic_storage_buffers += other.dynamic_storage_buffers;
511 self.sampled_textures.merge(&other.sampled_textures);
512 self.samplers.merge(&other.samplers);
513 self.storage_buffers.merge(&other.storage_buffers);
514 self.storage_textures.merge(&other.storage_textures);
515 self.uniform_buffers.merge(&other.uniform_buffers);
516 self.acceleration_structures
517 .merge(&other.acceleration_structures);
518 self.binding_array_elements
519 .merge(&other.binding_array_elements);
520 self.binding_array_sampler_elements
521 .merge(&other.binding_array_sampler_elements);
522 }
523
524 pub(crate) fn validate(&self, limits: &wgt::Limits) -> Result<(), BindingTypeMaxCountError> {
525 if limits.max_dynamic_uniform_buffers_per_pipeline_layout < self.dynamic_uniform_buffers {
526 return Err(BindingTypeMaxCountError {
527 kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,
528 zone: BindingZone::Pipeline,
529 limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,
530 count: self.dynamic_uniform_buffers,
531 });
532 }
533 if limits.max_dynamic_storage_buffers_per_pipeline_layout < self.dynamic_storage_buffers {
534 return Err(BindingTypeMaxCountError {
535 kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,
536 zone: BindingZone::Pipeline,
537 limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,
538 count: self.dynamic_storage_buffers,
539 });
540 }
541 self.sampled_textures.validate(
542 limits.max_sampled_textures_per_shader_stage,
543 BindingTypeMaxCountErrorKind::SampledTextures,
544 )?;
545 self.samplers.validate(
546 limits.max_samplers_per_shader_stage,
547 BindingTypeMaxCountErrorKind::Samplers,
548 )?;
549 self.storage_buffers.validate(
550 limits.max_storage_buffers_per_shader_stage,
551 BindingTypeMaxCountErrorKind::StorageBuffers,
552 )?;
553 self.storage_textures.validate(
554 limits.max_storage_textures_per_shader_stage,
555 BindingTypeMaxCountErrorKind::StorageTextures,
556 )?;
557 self.uniform_buffers.validate(
558 limits.max_uniform_buffers_per_shader_stage,
559 BindingTypeMaxCountErrorKind::UniformBuffers,
560 )?;
561 self.binding_array_elements.validate(
562 limits.max_binding_array_elements_per_shader_stage,
563 BindingTypeMaxCountErrorKind::BindingArrayElements,
564 )?;
565 self.binding_array_sampler_elements.validate(
566 limits.max_binding_array_sampler_elements_per_shader_stage,
567 BindingTypeMaxCountErrorKind::BindingArraySamplerElements,
568 )?;
569 self.acceleration_structures.validate(
570 limits.max_acceleration_structures_per_shader_stage,
571 BindingTypeMaxCountErrorKind::AccelerationStructures,
572 )?;
573 Ok(())
574 }
575
576 pub(crate) fn validate_binding_arrays(&self) -> Result<(), CreateBindGroupLayoutError> {
581 let has_dynamic_offset_array =
582 self.dynamic_uniform_buffers > 0 || self.dynamic_storage_buffers > 0;
583 let has_uniform_buffer = self.uniform_buffers.max().1 > 0;
584 if self.has_bindless_array && has_dynamic_offset_array {
585 return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndDynamicOffsetArray);
586 }
587 if self.has_bindless_array && has_uniform_buffer {
588 return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndUniformBuffer);
589 }
590 Ok(())
591 }
592}
593
594#[derive(Clone, Debug)]
597#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
598pub struct BindGroupEntry<
599 'a,
600 B = BufferId,
601 S = SamplerId,
602 TV = TextureViewId,
603 TLAS = TlasId,
604 ET = ExternalTextureId,
605> where
606 [BufferBinding<B>]: ToOwned,
607 [S]: ToOwned,
608 [TV]: ToOwned,
609 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
610 <[S] as ToOwned>::Owned: fmt::Debug,
611 <[TV] as ToOwned>::Owned: fmt::Debug,
612{
613 pub binding: u32,
616 #[cfg_attr(
617 feature = "serde",
618 serde(bound(deserialize = "BindingResource<'a, B, S, TV, TLAS, ET>: Deserialize<'de>"))
619 )]
620 pub resource: BindingResource<'a, B, S, TV, TLAS, ET>,
622}
623
624pub type ResolvedBindGroupEntry<'a> = BindGroupEntry<
626 'a,
627 Arc<Buffer>,
628 Arc<Sampler>,
629 Arc<TextureView>,
630 Arc<Tlas>,
631 Arc<ExternalTexture>,
632>;
633
634#[derive(Clone, Debug)]
636#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
637pub struct BindGroupDescriptor<
638 'a,
639 BGL = BindGroupLayoutId,
640 B = BufferId,
641 S = SamplerId,
642 TV = TextureViewId,
643 TLAS = TlasId,
644 ET = ExternalTextureId,
645> where
646 [BufferBinding<B>]: ToOwned,
647 [S]: ToOwned,
648 [TV]: ToOwned,
649 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
650 <[S] as ToOwned>::Owned: fmt::Debug,
651 <[TV] as ToOwned>::Owned: fmt::Debug,
652 [BindGroupEntry<'a, B, S, TV, TLAS, ET>]: ToOwned,
653 <[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: fmt::Debug,
654{
655 pub label: Label<'a>,
659 pub layout: BGL,
661 #[cfg_attr(
662 feature = "serde",
663 serde(bound(
664 deserialize = "<[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: Deserialize<'de>"
665 ))
666 )]
667 #[allow(clippy::type_complexity)]
669 pub entries: Cow<'a, [BindGroupEntry<'a, B, S, TV, TLAS, ET>]>,
670}
671
672pub type ResolvedBindGroupDescriptor<'a> = BindGroupDescriptor<
674 'a,
675 Arc<BindGroupLayout>,
676 Arc<Buffer>,
677 Arc<Sampler>,
678 Arc<TextureView>,
679 Arc<Tlas>,
680 Arc<ExternalTexture>,
681>;
682
683#[derive(Clone, Debug)]
685#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
686pub struct BindGroupLayoutDescriptor<'a> {
687 pub label: Label<'a>,
691 pub entries: Cow<'a, [wgt::BindGroupLayoutEntry]>,
693}
694
695#[derive(Debug)]
699pub(crate) enum ExclusivePipeline {
700 None,
701 Render(Weak<RenderPipeline>),
702 Compute(Weak<ComputePipeline>),
703}
704
705impl fmt::Display for ExclusivePipeline {
706 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
707 match self {
708 ExclusivePipeline::None => f.write_str("None"),
709 ExclusivePipeline::Render(p) => {
710 if let Some(p) = p.upgrade() {
711 p.error_ident().fmt(f)
712 } else {
713 f.write_str("RenderPipeline")
714 }
715 }
716 ExclusivePipeline::Compute(p) => {
717 if let Some(p) = p.upgrade() {
718 p.error_ident().fmt(f)
719 } else {
720 f.write_str("ComputePipeline")
721 }
722 }
723 }
724 }
725}
726
727#[derive(Debug)]
729pub struct BindGroupLayout {
730 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>,
731 pub(crate) device: Arc<Device>,
732 pub(crate) entries: bgl::EntryMap,
733 pub(crate) origin: bgl::Origin,
740 pub(crate) exclusive_pipeline: crate::OnceCellOrLock<ExclusivePipeline>,
741 #[allow(unused)]
742 pub(crate) binding_count_validator: BindingTypeMaxCountValidator,
743 pub(crate) label: String,
745}
746
747impl Drop for BindGroupLayout {
748 fn drop(&mut self) {
749 resource_log!("Destroy raw {}", self.error_ident());
750 if matches!(self.origin, bgl::Origin::Pool) {
751 self.device.bgl_pool.remove(&self.entries);
752 }
753 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
755 unsafe {
756 self.device.raw().destroy_bind_group_layout(raw);
757 }
758 }
759}
760
761crate::impl_resource_type!(BindGroupLayout);
762crate::impl_labeled!(BindGroupLayout);
763crate::impl_parent_device!(BindGroupLayout);
764crate::impl_storage_item!(BindGroupLayout);
765
766impl BindGroupLayout {
767 pub(crate) fn raw(&self) -> &dyn hal::DynBindGroupLayout {
768 self.raw.as_ref()
769 }
770}
771
772#[derive(Clone, Debug, Error)]
773#[non_exhaustive]
774pub enum CreatePipelineLayoutError {
775 #[error(transparent)]
776 Device(#[from] DeviceError),
777 #[error(
778 "Immediate data has range bound {size} which is not aligned to IMMEDIATE_DATA_ALIGNMENT ({})",
779 wgt::IMMEDIATE_DATA_ALIGNMENT
780 )]
781 MisalignedImmediateSize { size: u32 },
782 #[error(transparent)]
783 MissingFeatures(#[from] MissingFeatures),
784 #[error(
785 "Immediate data has size {size} which exceeds device immediate data size limit 0..{max}"
786 )]
787 ImmediateRangeTooLarge { size: u32, max: u32 },
788 #[error(transparent)]
789 TooManyBindings(BindingTypeMaxCountError),
790 #[error("Bind group layout count {actual} exceeds device bind group limit {max}")]
791 TooManyGroups { actual: usize, max: usize },
792 #[error(transparent)]
793 InvalidResource(#[from] InvalidResourceError),
794}
795
796impl WebGpuError for CreatePipelineLayoutError {
797 fn webgpu_error_type(&self) -> ErrorType {
798 let e: &dyn WebGpuError = match self {
799 Self::Device(e) => e,
800 Self::MissingFeatures(e) => e,
801 Self::InvalidResource(e) => e,
802 Self::TooManyBindings(e) => e,
803 Self::MisalignedImmediateSize { .. }
804 | Self::ImmediateRangeTooLarge { .. }
805 | Self::TooManyGroups { .. } => return ErrorType::Validation,
806 };
807 e.webgpu_error_type()
808 }
809}
810
811#[derive(Clone, Debug, Error)]
812#[non_exhaustive]
813pub enum ImmediateUploadError {
814 #[error("Provided immediate data written to offset {offset}..{end_offset} overruns the immediate data range with a size of {size}")]
815 TooLarge {
816 offset: u32,
817 end_offset: u32,
818 size: u32,
819 },
820 #[error(
821 "Provided immediate data offset {0} does not respect `IMMEDIATE_DATA_ALIGNMENT` ({ida})",
822 ida = wgt::IMMEDIATE_DATA_ALIGNMENT
823 )]
824 Unaligned(u32),
825}
826
827impl WebGpuError for ImmediateUploadError {
828 fn webgpu_error_type(&self) -> ErrorType {
829 ErrorType::Validation
830 }
831}
832
833#[derive(Clone, Debug, PartialEq, Eq, Hash)]
837#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
838#[cfg_attr(feature = "serde", serde(bound = "BGL: Serialize"))]
839pub struct PipelineLayoutDescriptor<'a, BGL = BindGroupLayoutId>
840where
841 [BGL]: ToOwned,
842 <[BGL] as ToOwned>::Owned: fmt::Debug,
843{
844 pub label: Label<'a>,
848 #[cfg_attr(
851 feature = "serde",
852 serde(bound(deserialize = "<[BGL] as ToOwned>::Owned: Deserialize<'de>"))
853 )]
854 pub bind_group_layouts: Cow<'a, [BGL]>,
855 pub immediate_size: u32,
861}
862
863pub type ResolvedPipelineLayoutDescriptor<'a, BGL = Arc<BindGroupLayout>> =
865 PipelineLayoutDescriptor<'a, BGL>;
866
867#[derive(Debug)]
868pub struct PipelineLayout {
869 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineLayout>>,
870 pub(crate) device: Arc<Device>,
871 pub(crate) label: String,
873 pub(crate) bind_group_layouts: ArrayVec<Arc<BindGroupLayout>, { hal::MAX_BIND_GROUPS }>,
874 pub(crate) immediate_size: u32,
875}
876
877impl Drop for PipelineLayout {
878 fn drop(&mut self) {
879 resource_log!("Destroy raw {}", self.error_ident());
880 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
882 unsafe {
883 self.device.raw().destroy_pipeline_layout(raw);
884 }
885 }
886}
887
888impl PipelineLayout {
889 pub(crate) fn raw(&self) -> &dyn hal::DynPipelineLayout {
890 self.raw.as_ref()
891 }
892
893 pub(crate) fn get_binding_maps(&self) -> ArrayVec<&bgl::EntryMap, { hal::MAX_BIND_GROUPS }> {
894 self.bind_group_layouts
895 .iter()
896 .map(|bgl| &bgl.entries)
897 .collect()
898 }
899
900 pub(crate) fn validate_immediates_ranges(
902 &self,
903 offset: u32,
904 end_offset: u32,
905 ) -> Result<(), ImmediateUploadError> {
906 if offset % wgt::IMMEDIATE_DATA_ALIGNMENT != 0 {
911 return Err(ImmediateUploadError::Unaligned(offset));
912 }
913
914 if end_offset > self.immediate_size {
915 return Err(ImmediateUploadError::TooLarge {
916 offset,
917 end_offset,
918 size: self.immediate_size,
919 });
920 }
921 Ok(())
922 }
923}
924
925crate::impl_resource_type!(PipelineLayout);
926crate::impl_labeled!(PipelineLayout);
927crate::impl_parent_device!(PipelineLayout);
928crate::impl_storage_item!(PipelineLayout);
929
930#[repr(C)]
931#[derive(Clone, Debug, Hash, Eq, PartialEq)]
932#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
933pub struct BufferBinding<B = BufferId> {
934 pub buffer: B,
935 pub offset: wgt::BufferAddress,
936
937 pub size: Option<wgt::BufferAddress>,
945}
946
947pub type ResolvedBufferBinding = BufferBinding<Arc<Buffer>>;
948
949#[derive(Debug, Clone)]
952#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
953pub enum BindingResource<
954 'a,
955 B = BufferId,
956 S = SamplerId,
957 TV = TextureViewId,
958 TLAS = TlasId,
959 ET = ExternalTextureId,
960> where
961 [BufferBinding<B>]: ToOwned,
962 [S]: ToOwned,
963 [TV]: ToOwned,
964 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
965 <[S] as ToOwned>::Owned: fmt::Debug,
966 <[TV] as ToOwned>::Owned: fmt::Debug,
967{
968 Buffer(BufferBinding<B>),
969 #[cfg_attr(
970 feature = "serde",
971 serde(bound(deserialize = "<[BufferBinding<B>] as ToOwned>::Owned: Deserialize<'de>"))
972 )]
973 BufferArray(Cow<'a, [BufferBinding<B>]>),
974 Sampler(S),
975 #[cfg_attr(
976 feature = "serde",
977 serde(bound(deserialize = "<[S] as ToOwned>::Owned: Deserialize<'de>"))
978 )]
979 SamplerArray(Cow<'a, [S]>),
980 TextureView(TV),
981 #[cfg_attr(
982 feature = "serde",
983 serde(bound(deserialize = "<[TV] as ToOwned>::Owned: Deserialize<'de>"))
984 )]
985 TextureViewArray(Cow<'a, [TV]>),
986 AccelerationStructure(TLAS),
987 ExternalTexture(ET),
988}
989
990pub type ResolvedBindingResource<'a> = BindingResource<
991 'a,
992 Arc<Buffer>,
993 Arc<Sampler>,
994 Arc<TextureView>,
995 Arc<Tlas>,
996 Arc<ExternalTexture>,
997>;
998
999#[derive(Clone, Debug, Error)]
1000#[non_exhaustive]
1001pub enum BindError {
1002 #[error(
1003 "{bind_group} {group} expects {expected} dynamic offset{s0}. However {actual} dynamic offset{s1} were provided.",
1004 s0 = if *.expected >= 2 { "s" } else { "" },
1005 s1 = if *.actual >= 2 { "s" } else { "" },
1006 )]
1007 MismatchedDynamicOffsetCount {
1008 bind_group: ResourceErrorIdent,
1009 group: u32,
1010 actual: usize,
1011 expected: usize,
1012 },
1013 #[error(
1014 "Dynamic binding index {idx} (targeting {bind_group} {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}"
1015 )]
1016 UnalignedDynamicBinding {
1017 bind_group: ResourceErrorIdent,
1018 idx: usize,
1019 group: u32,
1020 binding: u32,
1021 offset: u32,
1022 alignment: u32,
1023 limit_name: &'static str,
1024 },
1025 #[error(
1026 "Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to {bind_group} {group} -> binding {binding}. \
1027 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",
1028 )]
1029 DynamicBindingOutOfBounds {
1030 bind_group: ResourceErrorIdent,
1031 idx: usize,
1032 group: u32,
1033 binding: u32,
1034 offset: u32,
1035 buffer_size: wgt::BufferAddress,
1036 binding_range: Range<wgt::BufferAddress>,
1037 maximum_dynamic_offset: wgt::BufferAddress,
1038 },
1039}
1040
1041impl WebGpuError for BindError {
1042 fn webgpu_error_type(&self) -> ErrorType {
1043 ErrorType::Validation
1044 }
1045}
1046
1047#[derive(Debug)]
1048pub struct BindGroupDynamicBindingData {
1049 pub(crate) binding_idx: u32,
1053 pub(crate) buffer_size: wgt::BufferAddress,
1057 pub(crate) binding_range: Range<wgt::BufferAddress>,
1061 pub(crate) maximum_dynamic_offset: wgt::BufferAddress,
1063 pub(crate) binding_type: wgt::BufferBindingType,
1065}
1066
1067pub(crate) fn buffer_binding_type_alignment(
1068 limits: &wgt::Limits,
1069 binding_type: wgt::BufferBindingType,
1070) -> (u32, &'static str) {
1071 match binding_type {
1072 wgt::BufferBindingType::Uniform => (
1073 limits.min_uniform_buffer_offset_alignment,
1074 "min_uniform_buffer_offset_alignment",
1075 ),
1076 wgt::BufferBindingType::Storage { .. } => (
1077 limits.min_storage_buffer_offset_alignment,
1078 "min_storage_buffer_offset_alignment",
1079 ),
1080 }
1081}
1082
1083pub(crate) fn buffer_binding_type_bounds_check_alignment(
1084 alignments: &hal::Alignments,
1085 binding_type: wgt::BufferBindingType,
1086) -> wgt::BufferAddress {
1087 match binding_type {
1088 wgt::BufferBindingType::Uniform => alignments.uniform_bounds_check_alignment.get(),
1089 wgt::BufferBindingType::Storage { .. } => wgt::COPY_BUFFER_ALIGNMENT,
1090 }
1091}
1092
1093#[derive(Debug)]
1094pub(crate) struct BindGroupLateBufferBindingInfo {
1095 pub binding_index: u32,
1097 pub size: wgt::BufferSize,
1099}
1100
1101#[derive(Debug)]
1102pub struct BindGroup {
1103 pub(crate) raw: Snatchable<Box<dyn hal::DynBindGroup>>,
1104 pub(crate) device: Arc<Device>,
1105 pub(crate) layout: Arc<BindGroupLayout>,
1106 pub(crate) label: String,
1108 pub(crate) tracking_data: TrackingData,
1109 pub(crate) used: BindGroupStates,
1110 pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,
1111 pub(crate) used_texture_ranges: Vec<TextureInitTrackerAction>,
1112 pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
1113 pub(crate) late_buffer_binding_infos: Vec<BindGroupLateBufferBindingInfo>,
1116}
1117
1118impl Drop for BindGroup {
1119 fn drop(&mut self) {
1120 if let Some(raw) = self.raw.take() {
1121 resource_log!("Destroy raw {}", self.error_ident());
1122 unsafe {
1123 self.device.raw().destroy_bind_group(raw);
1124 }
1125 }
1126 }
1127}
1128
1129impl BindGroup {
1130 pub(crate) fn try_raw<'a>(
1131 &'a self,
1132 guard: &'a SnatchGuard,
1133 ) -> Result<&'a dyn hal::DynBindGroup, DestroyedResourceError> {
1134 for buffer in &self.used_buffer_ranges {
1137 buffer.buffer.try_raw(guard)?;
1138 }
1139 for texture in &self.used_texture_ranges {
1140 texture.texture.try_raw(guard)?;
1141 }
1142
1143 self.raw
1144 .get(guard)
1145 .map(|raw| raw.as_ref())
1146 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1147 }
1148
1149 pub(crate) fn validate_dynamic_bindings(
1150 &self,
1151 bind_group_index: u32,
1152 offsets: &[wgt::DynamicOffset],
1153 ) -> Result<(), BindError> {
1154 if self.dynamic_binding_info.len() != offsets.len() {
1155 return Err(BindError::MismatchedDynamicOffsetCount {
1156 bind_group: self.error_ident(),
1157 group: bind_group_index,
1158 expected: self.dynamic_binding_info.len(),
1159 actual: offsets.len(),
1160 });
1161 }
1162
1163 for (idx, (info, &offset)) in self
1164 .dynamic_binding_info
1165 .iter()
1166 .zip(offsets.iter())
1167 .enumerate()
1168 {
1169 let (alignment, limit_name) =
1170 buffer_binding_type_alignment(&self.device.limits, info.binding_type);
1171 if offset as wgt::BufferAddress % alignment as u64 != 0 {
1172 return Err(BindError::UnalignedDynamicBinding {
1173 bind_group: self.error_ident(),
1174 group: bind_group_index,
1175 binding: info.binding_idx,
1176 idx,
1177 offset,
1178 alignment,
1179 limit_name,
1180 });
1181 }
1182
1183 if offset as wgt::BufferAddress > info.maximum_dynamic_offset {
1184 return Err(BindError::DynamicBindingOutOfBounds {
1185 bind_group: self.error_ident(),
1186 group: bind_group_index,
1187 binding: info.binding_idx,
1188 idx,
1189 offset,
1190 buffer_size: info.buffer_size,
1191 binding_range: info.binding_range.clone(),
1192 maximum_dynamic_offset: info.maximum_dynamic_offset,
1193 });
1194 }
1195 }
1196
1197 Ok(())
1198 }
1199}
1200
1201crate::impl_resource_type!(BindGroup);
1202crate::impl_labeled!(BindGroup);
1203crate::impl_parent_device!(BindGroup);
1204crate::impl_storage_item!(BindGroup);
1205crate::impl_trackable!(BindGroup);
1206
1207#[derive(Clone, Debug, Error)]
1208#[non_exhaustive]
1209pub enum GetBindGroupLayoutError {
1210 #[error("Invalid group index {0}")]
1211 InvalidGroupIndex(u32),
1212 #[error(transparent)]
1213 InvalidResource(#[from] InvalidResourceError),
1214}
1215
1216impl WebGpuError for GetBindGroupLayoutError {
1217 fn webgpu_error_type(&self) -> ErrorType {
1218 match self {
1219 Self::InvalidGroupIndex(_) => ErrorType::Validation,
1220 Self::InvalidResource(e) => e.webgpu_error_type(),
1221 }
1222 }
1223}
1224
1225#[derive(Clone, Debug, Error, Eq, PartialEq)]
1226#[error(
1227 "In bind group index {group_index}, the buffer bound at binding index {binding_index} \
1228 is bound with size {bound_size} where the shader expects {shader_size}."
1229)]
1230pub struct LateMinBufferBindingSizeMismatch {
1231 pub group_index: u32,
1232 pub binding_index: u32,
1233 pub shader_size: wgt::BufferAddress,
1234 pub bound_size: wgt::BufferAddress,
1235}