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