1use alloc::{
2 borrow::{Cow, ToOwned},
3 boxed::Box,
4 string::String,
5 sync::{Arc, Weak},
6 vec::Vec,
7};
8use core::{
9 fmt,
10 mem::ManuallyDrop,
11 num::{NonZeroU32, Saturating},
12 ops::Range,
13};
14
15use arrayvec::ArrayVec;
16use thiserror::Error;
17
18#[cfg(feature = "serde")]
19use serde::Deserialize;
20#[cfg(feature = "serde")]
21use serde::Serialize;
22
23use wgpu_sync::OnceCell;
24use wgt::error::{ErrorType, WebGpuError};
25
26use crate::{
27 api_log,
28 device::{bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures},
29 init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
30 pipeline::{ComputePipeline, RenderPipeline},
31 resource::{
32 Buffer, DestroyedResourceError, ExternalTexture, InvalidOrDestroyedResourceError,
33 InvalidResourceError, Labeled, MissingBufferUsageError, MissingTextureUsageError,
34 RawResourceAccess, ResourceErrorIdent, ResourceState, Sampler, TextureView, Tlas,
35 TrackingData,
36 },
37 resource_log,
38 snatch::{SnatchGuard, Snatchable},
39 track::{BindGroupStates, ResourceUsageCompatibilityError},
40 Label,
41};
42
43#[derive(Clone, Debug, Error)]
44#[non_exhaustive]
45pub enum BindGroupLayoutEntryError {
46 #[error("Multiple binding types provided, expected exactly one")]
47 MultipleBindingTypesProvided,
48 #[error("No binding types provided, expected exactly one")]
49 NoBindingTypesProvided,
50 #[error("Cube dimension is not expected for texture storage")]
51 StorageTextureCube,
52 #[error("Atomic storage textures are not allowed by baseline webgpu, they require the native only feature TEXTURE_ATOMIC")]
53 StorageTextureAtomic,
54 #[error("Arrays of bindings unsupported for this type of binding")]
55 ArrayUnsupported,
56 #[error("Multisampled binding with sample type `TextureSampleType::Float` must have filterable set to false.")]
57 SampleTypeFloatFilterableBindingMultisampled,
58 #[error("Multisampled texture binding view dimension must be 2d, got {0:?}")]
59 Non2DMultisampled(wgt::TextureViewDimension),
60 #[error(transparent)]
61 MissingFeatures(#[from] MissingFeatures),
62 #[error(transparent)]
63 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
64}
65
66#[derive(Clone, Debug, Error)]
67#[non_exhaustive]
68pub enum CreateBindGroupLayoutError {
69 #[error(transparent)]
70 Device(#[from] DeviceError),
71 #[error("Conflicting binding at index {0}")]
72 ConflictBinding(u32),
73 #[error("Binding {binding} entry is invalid")]
74 Entry {
75 binding: u32,
76 #[source]
77 error: BindGroupLayoutEntryError,
78 },
79 #[error(transparent)]
80 TooManyBindings(BindingTypeMaxCountError),
81 #[error("Bind groups may not contain both a binding array and a dynamically offset buffer")]
82 ContainsBothBindingArrayAndDynamicOffsetArray,
83 #[error("Bind groups may not contain both a binding array and a uniform buffer")]
84 ContainsBothBindingArrayAndUniformBuffer,
85 #[error("Binding index {binding} is greater than the maximum number {maximum}")]
86 InvalidBindingIndex { binding: u32, maximum: u32 },
87 #[error("Invalid visibility {0:?}")]
88 InvalidVisibility(wgt::ShaderStages),
89 #[error("Binding index {binding}: {access:?} access to storage textures with format {format:?} is not supported")]
90 UnsupportedStorageTextureAccess {
91 binding: u32,
92 access: wgt::StorageTextureAccess,
93 format: wgt::TextureFormat,
94 },
95}
96
97impl WebGpuError for CreateBindGroupLayoutError {
98 fn webgpu_error_type(&self) -> ErrorType {
99 match self {
100 Self::Device(e) => e.webgpu_error_type(),
101
102 Self::ConflictBinding(_)
103 | Self::Entry { .. }
104 | Self::TooManyBindings(_)
105 | Self::InvalidBindingIndex { .. }
106 | Self::InvalidVisibility(_)
107 | Self::ContainsBothBindingArrayAndDynamicOffsetArray
108 | Self::ContainsBothBindingArrayAndUniformBuffer
109 | Self::UnsupportedStorageTextureAccess { .. } => ErrorType::Validation,
110 }
111 }
112}
113
114#[derive(Clone, Debug, Error)]
115#[non_exhaustive]
116pub enum BindingError {
117 #[error(transparent)]
118 DestroyedResource(#[from] DestroyedResourceError),
119 #[error("Buffer {buffer}: Binding with size {binding_size} at offset {offset} would overflow buffer size of {buffer_size}")]
120 BindingRangeTooLarge {
121 buffer: ResourceErrorIdent,
122 offset: wgt::BufferAddress,
123 binding_size: u64,
124 buffer_size: u64,
125 },
126 #[error("Buffer {buffer}: Binding offset {offset} is greater than buffer size {buffer_size}")]
127 BindingOffsetTooLarge {
128 buffer: ResourceErrorIdent,
129 offset: wgt::BufferAddress,
130 buffer_size: u64,
131 },
132 #[error("Buffer {buffer}: Binding offset {offset} must be strictly less than buffer size {buffer_size}")]
138 BindingOffsetEqualsSize {
139 buffer: ResourceErrorIdent,
140 offset: wgt::BufferAddress,
141 buffer_size: u64,
142 },
143 #[error("Unbinding vertex buffer at slot {slot} expects offset to be 0. However an offset of {offset} was provided.")]
144 UnbindingVertexBufferOffsetNotZero { slot: u32, offset: u64 },
145 #[error("Unbinding vertex buffer at slot {slot} expects size to be 0. However a size of {size} was provided.")]
146 UnbindingVertexBufferSizeNotZero { slot: u32, size: u64 },
147}
148
149impl WebGpuError for BindingError {
150 fn webgpu_error_type(&self) -> ErrorType {
151 match self {
152 Self::DestroyedResource(e) => e.webgpu_error_type(),
153 Self::BindingRangeTooLarge { .. }
154 | Self::BindingOffsetTooLarge { .. }
155 | Self::BindingOffsetEqualsSize { .. }
156 | BindingError::UnbindingVertexBufferOffsetNotZero { .. }
157 | BindingError::UnbindingVertexBufferSizeNotZero { .. } => ErrorType::Validation,
158 }
159 }
160}
161
162#[derive(Clone, Debug, Error)]
165#[non_exhaustive]
166pub enum CreateBindGroupError {
167 #[error(transparent)]
168 Device(#[from] DeviceError),
169 #[error(transparent)]
170 DestroyedResource(#[from] DestroyedResourceError),
171 #[error(transparent)]
172 BindingError(#[from] BindingError),
173 #[error(
174 "Binding count declared with at most {expected} items, but {actual} items were provided"
175 )]
176 BindingArrayPartialLengthMismatch { actual: usize, expected: usize },
177 #[error(
178 "Binding count declared with exactly {expected} items, but {actual} items were provided"
179 )]
180 BindingArrayLengthMismatch { actual: usize, expected: usize },
181 #[error("Array binding provided zero elements")]
182 BindingArrayZeroLength,
183 #[error("Binding size {actual} of {buffer} is less than minimum {min}")]
184 BindingSizeTooSmall {
185 buffer: ResourceErrorIdent,
186 actual: u64,
187 min: u64,
188 },
189 #[error("{0} binding size is zero")]
190 BindingZeroSize(ResourceErrorIdent),
191 #[error("Number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
192 BindingsNumMismatch { actual: usize, expected: usize },
193 #[error("Binding {0} is used at least twice in the descriptor")]
194 DuplicateBinding(u32),
195 #[error("Unable to find a corresponding declaration for the given binding {0}")]
196 MissingBindingDeclaration(u32),
197 #[error(transparent)]
198 MissingBufferUsage(#[from] MissingBufferUsageError),
199 #[error(transparent)]
200 MissingTextureUsage(#[from] MissingTextureUsageError),
201 #[error("Binding declared as a single item, but bind group is using it as an array")]
202 SingleBindingExpected,
203 #[error("Effective buffer binding size {size} for storage buffers is expected to align to {alignment}, but size is {size}")]
204 UnalignedEffectiveBufferBindingSizeForStorage { alignment: u32, size: u64 },
205 #[error("Buffer offset {0} does not respect device's requested `{1}` limit {2}")]
206 UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32),
207 #[error(
208 "Buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
209 )]
210 BufferRangeTooLarge {
211 binding: u32,
212 given: u64,
213 limit: u64,
214 },
215 #[error("Binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
216 WrongBindingType {
217 binding: u32,
219 actual: wgt::BindingType,
221 expected: &'static str,
223 },
224 #[error("Texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}")]
225 InvalidTextureMultisample {
226 binding: u32,
227 layout_multisampled: bool,
228 view_samples: u32,
229 },
230 #[error(
231 "Texture binding {} expects sample type {:?}, but was given a view with format {:?} (sample type {:?})",
232 binding,
233 layout_sample_type,
234 view_format,
235 view_sample_type
236 )]
237 InvalidTextureSampleType {
238 binding: u32,
239 layout_sample_type: wgt::TextureSampleType,
240 view_format: wgt::TextureFormat,
241 view_sample_type: wgt::TextureSampleType,
242 },
243 #[error("Texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}")]
244 InvalidTextureDimension {
245 binding: u32,
246 layout_dimension: wgt::TextureViewDimension,
247 view_dimension: wgt::TextureViewDimension,
248 },
249 #[error("Storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}")]
250 InvalidStorageTextureFormat {
251 binding: u32,
252 layout_format: wgt::TextureFormat,
253 view_format: wgt::TextureFormat,
254 },
255 #[error("Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
256 InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },
257 #[error("Storage texture bindings must have an identity swizzle, but given a view with swizzle = {swizzle:?}")]
258 InvalidStorageTextureSwizzle {
259 swizzle: wgt::TextureComponentSwizzle,
260 },
261 #[error("External texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
262 InvalidExternalTextureMipLevelCount { binding: u32, mip_level_count: u32 },
263 #[error("External texture bindings must have a format of `rgba8unorm`, `bgra8unorm`, or `rgba16float, but given a view with format = {format:?} at binding {binding}")]
264 InvalidExternalTextureFormat {
265 binding: u32,
266 format: wgt::TextureFormat,
267 },
268 #[error("Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}")]
269 WrongSamplerComparison {
270 binding: u32,
271 layout_cmp: bool,
272 sampler_cmp: bool,
273 },
274 #[error("Sampler binding {binding} expects filtering = {layout_flt}, but given a sampler with filtering = {sampler_flt}")]
275 WrongSamplerFiltering {
276 binding: u32,
277 layout_flt: bool,
278 sampler_flt: bool,
279 },
280 #[error("TLAS binding {binding} is required to support vertex returns but is missing flag AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN")]
281 MissingTLASVertexReturn { binding: u32 },
282 #[error("Bound texture views can not have both depth and stencil aspects enabled")]
283 DepthStencilAspect,
284 #[error(transparent)]
285 ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
286 #[error(transparent)]
287 InvalidResource(#[from] InvalidResourceError),
288}
289
290impl WebGpuError for CreateBindGroupError {
291 fn webgpu_error_type(&self) -> ErrorType {
292 match self {
293 Self::Device(e) => e.webgpu_error_type(),
294 Self::DestroyedResource(e) => e.webgpu_error_type(),
295 Self::BindingError(e) => e.webgpu_error_type(),
296 Self::MissingBufferUsage(e) => e.webgpu_error_type(),
297 Self::MissingTextureUsage(e) => e.webgpu_error_type(),
298 Self::ResourceUsageCompatibility(e) => e.webgpu_error_type(),
299 Self::InvalidResource(e) => e.webgpu_error_type(),
300 Self::BindingArrayPartialLengthMismatch { .. }
301 | Self::BindingArrayLengthMismatch { .. }
302 | Self::BindingArrayZeroLength
303 | Self::BindingSizeTooSmall { .. }
304 | Self::BindingsNumMismatch { .. }
305 | Self::BindingZeroSize(_)
306 | Self::DuplicateBinding(_)
307 | Self::MissingBindingDeclaration(_)
308 | Self::SingleBindingExpected
309 | Self::UnalignedEffectiveBufferBindingSizeForStorage { .. }
310 | Self::UnalignedBufferOffset(_, _, _)
311 | Self::BufferRangeTooLarge { .. }
312 | Self::WrongBindingType { .. }
313 | Self::InvalidTextureMultisample { .. }
314 | Self::InvalidTextureSampleType { .. }
315 | Self::InvalidTextureDimension { .. }
316 | Self::InvalidStorageTextureFormat { .. }
317 | Self::InvalidStorageTextureMipLevelCount { .. }
318 | Self::InvalidStorageTextureSwizzle { .. }
319 | Self::WrongSamplerComparison { .. }
320 | Self::WrongSamplerFiltering { .. }
321 | Self::DepthStencilAspect
322 | Self::MissingTLASVertexReturn { .. }
323 | Self::InvalidExternalTextureMipLevelCount { .. }
324 | Self::InvalidExternalTextureFormat { .. } => ErrorType::Validation,
325 }
326 }
327}
328
329#[derive(Clone, Debug, Error)]
330pub enum BindingZone {
331 #[error("Stage {0:?}")]
332 Stage(wgt::ShaderStages),
333 #[error("Whole pipeline")]
334 Pipeline,
335}
336
337#[derive(Clone, Debug, Error)]
338#[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())]
339pub struct BindingTypeMaxCountError {
340 pub kind: BindingTypeMaxCountErrorKind,
341 pub zone: BindingZone,
342 pub limit: u32,
343 pub count: u32,
344}
345
346impl WebGpuError for BindingTypeMaxCountError {
347 fn webgpu_error_type(&self) -> ErrorType {
348 ErrorType::Validation
349 }
350}
351
352#[derive(Clone, Debug)]
353pub enum BindingTypeMaxCountErrorKind {
354 DynamicUniformBuffers,
355 DynamicStorageBuffers,
356 SampledTextures,
357 Samplers,
358 StorageBuffers,
359 StorageTextures,
360 UniformBuffers,
361 BindingArrayElements,
362 BindingArraySamplerElements,
363 BindingArrayAccelerationStructureElements,
364 AccelerationStructures,
365 BuffersAndAccelerationStructures,
366}
367
368impl BindingTypeMaxCountErrorKind {
369 fn to_config_str(&self) -> &'static str {
370 match self {
371 BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {
372 "max_dynamic_uniform_buffers_per_pipeline_layout"
373 }
374 BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {
375 "max_dynamic_storage_buffers_per_pipeline_layout"
376 }
377 BindingTypeMaxCountErrorKind::SampledTextures => {
378 "max_sampled_textures_per_shader_stage"
379 }
380 BindingTypeMaxCountErrorKind::Samplers => "max_samplers_per_shader_stage",
381 BindingTypeMaxCountErrorKind::StorageBuffers => "max_storage_buffers_per_shader_stage",
382 BindingTypeMaxCountErrorKind::StorageTextures => {
383 "max_storage_textures_per_shader_stage"
384 }
385 BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
386 BindingTypeMaxCountErrorKind::BindingArrayElements => {
387 "max_binding_array_elements_per_shader_stage"
388 }
389 BindingTypeMaxCountErrorKind::BindingArraySamplerElements => {
390 "max_binding_array_sampler_elements_per_shader_stage"
391 }
392 BindingTypeMaxCountErrorKind::BindingArrayAccelerationStructureElements => {
393 "max_binding_array_acceleration_structure_elements_per_shader_stage"
394 }
395 BindingTypeMaxCountErrorKind::AccelerationStructures => {
396 "max_acceleration_structures_per_shader_stage"
397 }
398 BindingTypeMaxCountErrorKind::BuffersAndAccelerationStructures => {
399 "max_buffers_and_acceleration_structures_per_shader_stage"
400 }
401 }
402 }
403}
404
405#[derive(Debug, Default)]
406pub(crate) struct PerStageBindingTypeCounter {
407 vertex: Saturating<u32>,
408 fragment: Saturating<u32>,
409 compute: Saturating<u32>,
410}
411
412impl PerStageBindingTypeCounter {
413 pub(crate) fn add(&mut self, stage: wgt::ShaderStages, count: u32) {
414 if stage.contains(wgt::ShaderStages::VERTEX) {
415 self.vertex += count;
416 }
417 if stage.contains(wgt::ShaderStages::FRAGMENT) {
418 self.fragment += count;
419 }
420 if stage.contains(wgt::ShaderStages::COMPUTE) {
421 self.compute += count;
422 }
423 }
424
425 pub(crate) fn max(&self) -> (BindingZone, u32) {
426 let max_value = self.vertex.max(self.fragment.max(self.compute));
427 let mut stage = wgt::ShaderStages::NONE;
428 if max_value == self.vertex {
429 stage |= wgt::ShaderStages::VERTEX
430 }
431 if max_value == self.fragment {
432 stage |= wgt::ShaderStages::FRAGMENT
433 }
434 if max_value == self.compute {
435 stage |= wgt::ShaderStages::COMPUTE
436 }
437 (BindingZone::Stage(stage), max_value.0)
438 }
439
440 pub(crate) fn merge(&mut self, other: &Self) {
441 self.vertex += other.vertex;
442 self.fragment += other.fragment;
443 self.compute += other.compute;
444 }
445
446 pub(crate) fn validate(
447 &self,
448 limit: u32,
449 kind: BindingTypeMaxCountErrorKind,
450 ) -> Result<(), BindingTypeMaxCountError> {
451 let (zone, count) = self.max();
452 if limit < count {
453 Err(BindingTypeMaxCountError {
454 kind,
455 zone,
456 limit,
457 count,
458 })
459 } else {
460 Ok(())
461 }
462 }
463}
464
465#[derive(Debug, Default)]
466pub(crate) struct BindingTypeMaxCountValidator {
467 dynamic_uniform_buffers: u32,
468 dynamic_storage_buffers: u32,
469 sampled_textures: PerStageBindingTypeCounter,
470 samplers: PerStageBindingTypeCounter,
471 storage_buffers: PerStageBindingTypeCounter,
472 storage_textures: PerStageBindingTypeCounter,
473 uniform_buffers: PerStageBindingTypeCounter,
474 acceleration_structures: PerStageBindingTypeCounter,
475 binding_array_elements: PerStageBindingTypeCounter,
476 binding_array_sampler_elements: PerStageBindingTypeCounter,
477 binding_array_acceleration_structure_elements: PerStageBindingTypeCounter,
478 has_bindless_array: bool,
479}
480
481impl BindingTypeMaxCountValidator {
482 pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {
483 let count = binding.count.map_or(1, |count| count.get());
484
485 if binding.count.is_some() {
486 self.binding_array_elements.add(binding.visibility, count);
487 self.has_bindless_array = true;
488
489 match binding.ty {
490 wgt::BindingType::Sampler(_) => {
491 self.binding_array_sampler_elements
492 .add(binding.visibility, count);
493 }
494 wgt::BindingType::AccelerationStructure { .. } => {
495 self.binding_array_acceleration_structure_elements
496 .add(binding.visibility, count);
497 }
498 _ => {}
499 }
500 } else {
501 match binding.ty {
502 wgt::BindingType::Buffer {
503 ty: wgt::BufferBindingType::Uniform,
504 has_dynamic_offset,
505 ..
506 } => {
507 self.uniform_buffers.add(binding.visibility, count);
508 if has_dynamic_offset {
509 self.dynamic_uniform_buffers += count;
510 }
511 }
512 wgt::BindingType::Buffer {
513 ty: wgt::BufferBindingType::Storage { .. },
514 has_dynamic_offset,
515 ..
516 } => {
517 self.storage_buffers.add(binding.visibility, count);
518 if has_dynamic_offset {
519 self.dynamic_storage_buffers += count;
520 }
521 }
522 wgt::BindingType::Sampler { .. } => {
523 self.samplers.add(binding.visibility, count);
524 }
525 wgt::BindingType::Texture { .. } => {
526 self.sampled_textures.add(binding.visibility, count);
527 }
528 wgt::BindingType::StorageTexture { .. } => {
529 self.storage_textures.add(binding.visibility, count);
530 }
531 wgt::BindingType::AccelerationStructure { .. } => {
532 self.acceleration_structures.add(binding.visibility, count);
533 }
534 wgt::BindingType::ExternalTexture => {
535 self.sampled_textures.add(binding.visibility, count * 4);
544 self.samplers.add(binding.visibility, count);
545 self.uniform_buffers.add(binding.visibility, count);
546 }
547 }
548 }
549 }
550
551 pub(crate) fn merge(&mut self, other: &Self) {
552 self.dynamic_uniform_buffers += other.dynamic_uniform_buffers;
553 self.dynamic_storage_buffers += other.dynamic_storage_buffers;
554 self.sampled_textures.merge(&other.sampled_textures);
555 self.samplers.merge(&other.samplers);
556 self.storage_buffers.merge(&other.storage_buffers);
557 self.storage_textures.merge(&other.storage_textures);
558 self.uniform_buffers.merge(&other.uniform_buffers);
559 self.acceleration_structures
560 .merge(&other.acceleration_structures);
561 self.binding_array_elements
562 .merge(&other.binding_array_elements);
563 self.binding_array_sampler_elements
564 .merge(&other.binding_array_sampler_elements);
565 self.binding_array_acceleration_structure_elements
566 .merge(&other.binding_array_acceleration_structure_elements);
567 }
568
569 pub(crate) fn validate(
570 &self,
571 limits: &wgt::Limits,
572 instance_flags: wgt::InstanceFlags,
573 ) -> Result<(), BindingTypeMaxCountError> {
574 if limits.max_dynamic_uniform_buffers_per_pipeline_layout < self.dynamic_uniform_buffers {
575 return Err(BindingTypeMaxCountError {
576 kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,
577 zone: BindingZone::Pipeline,
578 limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,
579 count: self.dynamic_uniform_buffers,
580 });
581 }
582 if limits.max_dynamic_storage_buffers_per_pipeline_layout < self.dynamic_storage_buffers {
583 return Err(BindingTypeMaxCountError {
584 kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,
585 zone: BindingZone::Pipeline,
586 limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,
587 count: self.dynamic_storage_buffers,
588 });
589 }
590 self.sampled_textures.validate(
591 limits.max_sampled_textures_per_shader_stage,
592 BindingTypeMaxCountErrorKind::SampledTextures,
593 )?;
594 self.samplers.validate(
595 limits.max_samplers_per_shader_stage,
596 BindingTypeMaxCountErrorKind::Samplers,
597 )?;
598 self.storage_buffers.validate(
599 limits.max_storage_buffers_per_shader_stage,
600 BindingTypeMaxCountErrorKind::StorageBuffers,
601 )?;
602 self.storage_textures.validate(
607 limits.max_storage_textures_per_shader_stage,
608 BindingTypeMaxCountErrorKind::StorageTextures,
609 )?;
610 self.uniform_buffers.validate(
615 limits.max_uniform_buffers_per_shader_stage,
616 BindingTypeMaxCountErrorKind::UniformBuffers,
617 )?;
618 self.binding_array_elements.validate(
619 limits.max_binding_array_elements_per_shader_stage,
620 BindingTypeMaxCountErrorKind::BindingArrayElements,
621 )?;
622 self.binding_array_sampler_elements.validate(
623 limits.max_binding_array_sampler_elements_per_shader_stage,
624 BindingTypeMaxCountErrorKind::BindingArraySamplerElements,
625 )?;
626 self.binding_array_acceleration_structure_elements
627 .validate(
628 limits.max_binding_array_acceleration_structure_elements_per_shader_stage,
629 BindingTypeMaxCountErrorKind::BindingArrayAccelerationStructureElements,
630 )?;
631 self.acceleration_structures.validate(
632 limits.max_acceleration_structures_per_shader_stage,
633 BindingTypeMaxCountErrorKind::AccelerationStructures,
634 )?;
635
636 if !instance_flags.contains(wgt::InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
637 self.buffers_and_acceleration_structures().validate(
638 limits.max_buffers_and_acceleration_structures_per_shader_stage,
639 BindingTypeMaxCountErrorKind::BuffersAndAccelerationStructures,
640 )?;
641 }
642
643 Ok(())
644 }
645
646 fn buffers_and_acceleration_structures(&self) -> PerStageBindingTypeCounter {
647 let mut buffers_and_acceleration_structures = PerStageBindingTypeCounter::default();
648 buffers_and_acceleration_structures.merge(&self.uniform_buffers);
649 buffers_and_acceleration_structures.merge(&self.storage_buffers);
650 buffers_and_acceleration_structures.merge(&self.acceleration_structures);
651 buffers_and_acceleration_structures
652 }
653
654 pub(crate) fn buffers_and_acceleration_structures_in_vertex_stage(&self) -> u32 {
655 self.buffers_and_acceleration_structures().vertex.0
656 }
657
658 pub(crate) fn validate_binding_arrays(&self) -> Result<(), CreateBindGroupLayoutError> {
663 let has_dynamic_offset_array =
664 self.dynamic_uniform_buffers > 0 || self.dynamic_storage_buffers > 0;
665 let has_uniform_buffer = self.uniform_buffers.max().1 > 0;
666 if self.has_bindless_array && has_dynamic_offset_array {
667 return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndDynamicOffsetArray);
668 }
669 if self.has_bindless_array && has_uniform_buffer {
670 return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndUniformBuffer);
671 }
672 Ok(())
673 }
674}
675
676#[derive(Clone, Debug)]
679#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
680pub struct BindGroupEntry<
681 'a,
682 B = Arc<Buffer>,
683 S = Arc<Sampler>,
684 TV = Arc<TextureView>,
685 TLAS = Arc<Tlas>,
686 ET = Arc<ExternalTexture>,
687> where
688 [BufferBinding<B>]: ToOwned,
689 [S]: ToOwned,
690 [TV]: ToOwned,
691 [TLAS]: ToOwned,
692 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
693 <[S] as ToOwned>::Owned: fmt::Debug,
694 <[TV] as ToOwned>::Owned: fmt::Debug,
695 <[TLAS] as ToOwned>::Owned: fmt::Debug,
696{
697 pub binding: u32,
700 #[cfg_attr(
701 feature = "serde",
702 serde(bound(deserialize = "BindingResource<'a, B, S, TV, TLAS, ET>: Deserialize<'de>"))
703 )]
704 pub resource: BindingResource<'a, B, S, TV, TLAS, ET>,
706}
707
708#[derive(Clone, Debug)]
710#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
711pub struct BindGroupDescriptor<
713 'a,
714 BGL = Arc<BindGroupLayout>,
715 B = Arc<Buffer>,
716 S = Arc<Sampler>,
717 TV = Arc<TextureView>,
718 TLAS = Arc<Tlas>,
719 ET = Arc<ExternalTexture>,
720> where
721 [BufferBinding<B>]: ToOwned,
722 [S]: ToOwned,
723 [TV]: ToOwned,
724 [TLAS]: ToOwned,
725 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
726 <[S] as ToOwned>::Owned: fmt::Debug,
727 <[TV] as ToOwned>::Owned: fmt::Debug,
728 <[TLAS] as ToOwned>::Owned: fmt::Debug,
729 [BindGroupEntry<'a, B, S, TV, TLAS, ET>]: ToOwned,
730 <[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: fmt::Debug,
731{
732 pub label: Label<'a>,
736 pub layout: BGL,
738 #[cfg_attr(
739 feature = "serde",
740 serde(bound(
741 deserialize = "<[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: Deserialize<'de>"
742 ))
743 )]
744 #[allow(clippy::type_complexity)]
746 pub entries: Cow<'a, [BindGroupEntry<'a, B, S, TV, TLAS, ET>]>,
747}
748
749#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
754#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
755pub struct BufferBindingLayout {
756 pub ty: wgt::BufferBindingType,
758
759 #[cfg_attr(feature = "serde", serde(default))]
765 pub has_dynamic_offset: bool,
766
767 #[cfg_attr(feature = "serde", serde(default))]
788 pub min_binding_size: Option<wgt::BufferSize>,
789}
790
791#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
808#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
809pub struct SamplerBindingLayout {
810 pub ty: wgt::SamplerBindingType,
812}
813
814#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
831#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
832pub struct TextureBindingLayout {
833 pub sample_type: wgt::TextureSampleType,
835 pub view_dimension: wgt::TextureViewDimension,
837 pub multisampled: bool,
841}
842
843#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
862#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
863pub struct StorageTextureBindingLayout {
864 pub access: wgt::StorageTextureAccess,
866 pub format: wgt::TextureFormat,
868 pub view_dimension: wgt::TextureViewDimension,
870}
871
872#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
892#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
893pub struct AccelerationStructureBindingLayout {
894 pub vertex_return: bool,
899}
900
901#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
914#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
915pub struct ExternalTextureBindingLayout;
916
917#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
922#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
923pub struct BindGroupLayoutEntry {
924 pub binding: u32,
927 pub visibility: wgt::ShaderStages,
929
930 #[cfg_attr(feature = "serde", serde(default))]
932 pub buffer: Option<BufferBindingLayout>,
933 #[cfg_attr(feature = "serde", serde(default))]
934 pub sampler: Option<SamplerBindingLayout>,
935 #[cfg_attr(feature = "serde", serde(default))]
936 pub texture: Option<TextureBindingLayout>,
937 #[cfg_attr(feature = "serde", serde(default))]
938 pub storage_texture: Option<StorageTextureBindingLayout>,
939 #[cfg_attr(feature = "serde", serde(default))]
940 pub external_texture: Option<ExternalTextureBindingLayout>,
941 #[cfg_attr(feature = "serde", serde(default))]
942 pub acceleration_structure: Option<AccelerationStructureBindingLayout>,
943
944 #[cfg_attr(feature = "serde", serde(default))]
958 pub count: Option<NonZeroU32>,
959}
960
961impl From<core::convert::Infallible> for CreateBindGroupLayoutError {
962 fn from(_: core::convert::Infallible) -> Self {
963 unreachable!()
964 }
965}
966
967impl From<wgt::BindGroupLayoutEntry> for BindGroupLayoutEntry {
968 fn from(val: wgt::BindGroupLayoutEntry) -> Self {
969 let wgt::BindGroupLayoutEntry {
970 binding,
971 visibility,
972 ty,
973 count,
974 } = val;
975 BindGroupLayoutEntry {
976 binding,
977 visibility,
978 buffer: if let wgt::BindingType::Buffer {
979 ty,
980 has_dynamic_offset,
981 min_binding_size,
982 } = ty
983 {
984 Some(BufferBindingLayout {
985 ty,
986 has_dynamic_offset,
987 min_binding_size,
988 })
989 } else {
990 None
991 },
992 sampler: if let wgt::BindingType::Sampler(ty) = ty {
993 Some(SamplerBindingLayout { ty })
994 } else {
995 None
996 },
997 texture: if let wgt::BindingType::Texture {
998 sample_type,
999 view_dimension,
1000 multisampled,
1001 } = ty
1002 {
1003 Some(TextureBindingLayout {
1004 sample_type,
1005 view_dimension,
1006 multisampled,
1007 })
1008 } else {
1009 None
1010 },
1011 storage_texture: if let wgt::BindingType::StorageTexture {
1012 access,
1013 format,
1014 view_dimension,
1015 } = ty
1016 {
1017 Some(StorageTextureBindingLayout {
1018 access,
1019 format,
1020 view_dimension,
1021 })
1022 } else {
1023 None
1024 },
1025 external_texture: if let wgt::BindingType::ExternalTexture = ty {
1026 Some(ExternalTextureBindingLayout {})
1027 } else {
1028 None
1029 },
1030 acceleration_structure: if let wgt::BindingType::AccelerationStructure {
1031 vertex_return,
1032 } = ty
1033 {
1034 Some(AccelerationStructureBindingLayout { vertex_return })
1035 } else {
1036 None
1037 },
1038 count,
1039 }
1040 }
1041}
1042
1043impl TryInto<wgt::BindGroupLayoutEntry> for BindGroupLayoutEntry {
1044 type Error = CreateBindGroupLayoutError;
1045
1046 fn try_into(self) -> Result<wgt::BindGroupLayoutEntry, Self::Error> {
1047 let BindGroupLayoutEntry {
1048 binding,
1049 visibility,
1050 buffer,
1051 sampler,
1052 texture,
1053 storage_texture,
1054 external_texture,
1055 acceleration_structure,
1056 count,
1057 } = self;
1058 let mut binding_ty: Option<wgt::BindingType> = None;
1059 if let Some(BufferBindingLayout {
1060 ty,
1061 has_dynamic_offset,
1062 min_binding_size,
1063 }) = buffer
1064 {
1065 binding_ty = Some(wgt::BindingType::Buffer {
1066 ty,
1067 has_dynamic_offset,
1068 min_binding_size,
1069 });
1070 }
1071 if let Some(SamplerBindingLayout { ty }) = sampler {
1072 if binding_ty.is_some() {
1073 return Err(CreateBindGroupLayoutError::Entry {
1074 binding,
1075 error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1076 });
1077 }
1078 binding_ty = Some(wgt::BindingType::Sampler(ty))
1079 }
1080 if let Some(TextureBindingLayout {
1081 sample_type,
1082 view_dimension,
1083 multisampled,
1084 }) = texture
1085 {
1086 if binding_ty.is_some() {
1087 return Err(CreateBindGroupLayoutError::Entry {
1088 binding,
1089 error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1090 });
1091 }
1092 binding_ty = Some(wgt::BindingType::Texture {
1093 sample_type,
1094 view_dimension,
1095 multisampled,
1096 })
1097 }
1098 if let Some(StorageTextureBindingLayout {
1099 access,
1100 format,
1101 view_dimension,
1102 }) = storage_texture
1103 {
1104 if binding_ty.is_some() {
1105 return Err(CreateBindGroupLayoutError::Entry {
1106 binding,
1107 error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1108 });
1109 }
1110 binding_ty = Some(wgt::BindingType::StorageTexture {
1111 access,
1112 format,
1113 view_dimension,
1114 })
1115 }
1116 if let Some(ExternalTextureBindingLayout) = external_texture {
1117 if binding_ty.is_some() {
1118 return Err(CreateBindGroupLayoutError::Entry {
1119 binding,
1120 error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1121 });
1122 }
1123 binding_ty = Some(wgt::BindingType::ExternalTexture)
1124 }
1125 if let Some(AccelerationStructureBindingLayout { vertex_return }) = acceleration_structure {
1126 if binding_ty.is_some() {
1127 return Err(CreateBindGroupLayoutError::Entry {
1128 binding,
1129 error: BindGroupLayoutEntryError::MultipleBindingTypesProvided,
1130 });
1131 }
1132 binding_ty = Some(wgt::BindingType::AccelerationStructure { vertex_return })
1133 }
1134 Ok(wgt::BindGroupLayoutEntry {
1135 binding,
1136 visibility,
1137 ty: binding_ty.ok_or(CreateBindGroupLayoutError::Entry {
1138 binding,
1139 error: BindGroupLayoutEntryError::NoBindingTypesProvided,
1140 })?,
1141 count,
1142 })
1143 }
1144}
1145
1146#[derive(Clone, Debug)]
1148#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1149pub struct BindGroupLayoutDescriptor<'a, BGLE: Copy> {
1150 pub label: Label<'a>,
1154 pub entries: Cow<'a, [BGLE]>,
1156}
1157
1158#[derive(Clone, Debug)]
1162pub(crate) enum ExclusivePipeline {
1163 None,
1164 Render(Weak<RenderPipeline>),
1165 Compute(Weak<ComputePipeline>),
1166}
1167
1168impl From<&Arc<RenderPipeline>> for ExclusivePipeline {
1169 fn from(pipeline: &Arc<RenderPipeline>) -> Self {
1170 Self::Render(Arc::downgrade(pipeline))
1171 }
1172}
1173
1174impl From<&Arc<ComputePipeline>> for ExclusivePipeline {
1175 fn from(pipeline: &Arc<ComputePipeline>) -> Self {
1176 Self::Compute(Arc::downgrade(pipeline))
1177 }
1178}
1179
1180impl fmt::Display for ExclusivePipeline {
1181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1182 match self {
1183 ExclusivePipeline::None => f.write_str("None"),
1184 ExclusivePipeline::Render(p) => {
1185 if let Some(p) = p.upgrade() {
1186 p.error_ident().fmt(f)
1187 } else {
1188 f.write_str("RenderPipeline")
1189 }
1190 }
1191 ExclusivePipeline::Compute(p) => {
1192 if let Some(p) = p.upgrade() {
1193 p.error_ident().fmt(f)
1194 } else {
1195 f.write_str("ComputePipeline")
1196 }
1197 }
1198 }
1199 }
1200}
1201
1202#[derive(Debug)]
1203pub enum RawBindGroupLayout {
1204 Owning(ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>),
1205 RefDeviceEmptyBGL,
1207}
1208
1209#[derive(Debug)]
1210pub(crate) struct BindGroupLayoutState {
1211 pub(crate) raw: RawBindGroupLayout,
1212 pub(crate) origin: bgl::Origin,
1219 pub(crate) binding_count_validator: BindingTypeMaxCountValidator,
1220}
1221
1222#[derive(Debug)]
1224pub struct BindGroupLayout {
1225 pub(crate) state: ResourceState<BindGroupLayoutState>,
1226 pub(crate) device: Arc<Device>,
1227 pub(crate) entries: bgl::EntryMap,
1228 pub(crate) exclusive_pipeline: OnceCell<ExclusivePipeline>,
1229 pub(crate) label: String,
1231}
1232
1233impl Drop for BindGroupLayout {
1234 #[allow(trivial_casts)]
1235 fn drop(&mut self) {
1236 profiling::scope!("BindGroupLayout::drop");
1237 api_log!("BindGroupLayout::drop {:?}", self as *const _);
1238 #[cfg(feature = "trace")]
1239 {
1240 let mut t = self.device.trace.lock();
1241 if let Some(t) = t.as_mut() {
1242 use crate::device::trace;
1243
1244 t.add(trace::Action::DropBindGroupLayout(unsafe {
1246 trace::to_trace(self)
1247 }));
1248 }
1249 }
1250 resource_log!("Destroy raw {}", self.error_ident());
1251 let ResourceState::Valid(state) = &mut self.state else {
1252 return;
1253 };
1254 if matches!(state.origin, bgl::Origin::Pool) {
1255 self.device.bgl_pool.remove(&self.entries);
1256 }
1257 match state.raw {
1258 RawBindGroupLayout::Owning(ref mut raw) => {
1259 let raw = unsafe { ManuallyDrop::take(raw) };
1261 unsafe {
1262 self.device.raw().destroy_bind_group_layout(raw);
1263 }
1264 }
1265 RawBindGroupLayout::RefDeviceEmptyBGL => {}
1266 }
1267 }
1268}
1269
1270crate::impl_resource_type!(BindGroupLayout);
1271crate::impl_labeled!(BindGroupLayout);
1272crate::impl_parent_device!(BindGroupLayout);
1273crate::impl_storage_item!(BindGroupLayout);
1274
1275impl BindGroupLayout {
1276 pub(crate) fn try_raw(&self) -> Result<&dyn hal::DynBindGroupLayout, InvalidResourceError> {
1277 let ResourceState::Valid(state) = &self.state else {
1278 return Err(InvalidResourceError(self.error_ident()));
1279 };
1280 match &state.raw {
1281 RawBindGroupLayout::Owning(raw) => Ok(raw.as_ref()),
1282 RawBindGroupLayout::RefDeviceEmptyBGL => Ok(self.device.empty_bgl.as_ref()),
1283 }
1284 }
1285
1286 pub(crate) fn state(&self) -> Result<&BindGroupLayoutState, InvalidResourceError> {
1287 let ResourceState::Valid(state) = &self.state else {
1288 return Err(InvalidResourceError(self.error_ident()));
1289 };
1290 Ok(state)
1291 }
1292
1293 pub(crate) fn check_is_valid(self: &Arc<Self>) -> Result<(), InvalidResourceError> {
1294 let ResourceState::Valid(_) = &self.state else {
1295 return Err(InvalidResourceError(self.error_ident()));
1296 };
1297 Ok(())
1298 }
1299
1300 fn empty(device: &Arc<Device>, exclusive_pipeline: ExclusivePipeline) -> Arc<Self> {
1301 Arc::new(Self {
1302 state: ResourceState::Valid(BindGroupLayoutState {
1303 raw: RawBindGroupLayout::RefDeviceEmptyBGL,
1304 origin: bgl::Origin::Derived,
1305 binding_count_validator: BindingTypeMaxCountValidator::default(),
1306 }),
1307 device: device.clone(),
1308 entries: bgl::EntryMap::default(),
1309 exclusive_pipeline: OnceCell::from(exclusive_pipeline),
1310 label: String::new(),
1311 })
1312 }
1313
1314 pub fn invalid(device: &Arc<Device>, label: String) -> Arc<Self> {
1315 Arc::new(Self {
1316 state: ResourceState::Invalid,
1317 device: device.clone(),
1318 entries: bgl::EntryMap::default(),
1319 exclusive_pipeline: OnceCell::from(ExclusivePipeline::None),
1320 label,
1321 })
1322 }
1323}
1324
1325#[derive(Clone, Debug, Error)]
1326#[non_exhaustive]
1327pub enum CreatePipelineLayoutError {
1328 #[error(transparent)]
1329 Device(#[from] DeviceError),
1330 #[error(
1331 "Immediate data has range bound {size} which is not aligned to IMMEDIATE_DATA_ALIGNMENT ({})",
1332 wgt::IMMEDIATE_DATA_ALIGNMENT
1333 )]
1334 MisalignedImmediateSize { size: u32 },
1335 #[error(transparent)]
1336 MissingFeatures(#[from] MissingFeatures),
1337 #[error(
1338 "Immediate data has size {size} which exceeds device immediate data size limit 0..{max}"
1339 )]
1340 ImmediateRangeTooLarge { size: u32, max: u32 },
1341 #[error(transparent)]
1342 TooManyBindings(BindingTypeMaxCountError),
1343 #[error("Bind group layout count {actual} exceeds device bind group limit {max}")]
1344 TooManyGroups { actual: usize, max: usize },
1345 #[error(transparent)]
1346 InvalidResource(#[from] InvalidResourceError),
1347 #[error("Bind group layout at index {index} has an exclusive pipeline: {pipeline}")]
1348 BglHasExclusivePipeline { index: usize, pipeline: String },
1349}
1350
1351impl WebGpuError for CreatePipelineLayoutError {
1352 fn webgpu_error_type(&self) -> ErrorType {
1353 match self {
1354 Self::Device(e) => e.webgpu_error_type(),
1355 Self::MissingFeatures(e) => e.webgpu_error_type(),
1356 Self::InvalidResource(e) => e.webgpu_error_type(),
1357 Self::TooManyBindings(e) => e.webgpu_error_type(),
1358 Self::MisalignedImmediateSize { .. }
1359 | Self::ImmediateRangeTooLarge { .. }
1360 | Self::TooManyGroups { .. }
1361 | Self::BglHasExclusivePipeline { .. } => ErrorType::Validation,
1362 }
1363 }
1364}
1365
1366#[derive(Clone, Debug, Error)]
1367#[non_exhaustive]
1368pub enum ImmediateUploadError {
1369 #[error(
1370 "Provided immediate data start offset {start_offset} overruns the range with a size of {immediate_size}"
1371 )]
1372 StartOffsetOverrun {
1373 start_offset: u32,
1374 immediate_size: u32,
1375 },
1376 #[error(
1377 "Provided immediate data start offset {0} does not respect \
1378 `IMMEDIATE_DATA_ALIGNMENT` ({ida})",
1379 ida = wgt::IMMEDIATE_DATA_ALIGNMENT
1380 )]
1381 StartOffsetUnaligned(u32),
1382 #[error(
1383 "Provided immediate data byte size {0} does not respect \
1384 `IMMEDIATE_DATA_ALIGNMENT` ({ida})",
1385 ida = wgt::IMMEDIATE_DATA_ALIGNMENT
1386 )]
1387 SizeUnaligned(usize),
1388 #[error(
1389 "Provided immediate data start offset {} + size {} overruns `max_immediate_size` {}",
1390 start_offset,
1391 size_bytes,
1392 limit
1393 )]
1394 EndOffsetBeyondLimit {
1395 start_offset: u32,
1396 size_bytes: usize,
1397 limit: u32,
1398 },
1399}
1400
1401impl WebGpuError for ImmediateUploadError {
1402 fn webgpu_error_type(&self) -> ErrorType {
1403 ErrorType::Validation
1404 }
1405}
1406
1407#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1411#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1412#[cfg_attr(feature = "serde", serde(bound = "BGL: Serialize"))]
1413pub struct PipelineLayoutDescriptor<'a, BGL = Arc<BindGroupLayout>>
1415where
1416 [Option<BGL>]: ToOwned,
1417 <[Option<BGL>] as ToOwned>::Owned: fmt::Debug,
1418{
1419 pub label: Label<'a>,
1423 #[cfg_attr(
1426 feature = "serde",
1427 serde(bound(deserialize = "<[Option<BGL>] as ToOwned>::Owned: Deserialize<'de>"))
1428 )]
1429 pub bind_group_layouts: Cow<'a, [Option<BGL>]>,
1430 pub immediate_size: u32,
1436}
1437
1438#[derive(Debug)]
1439pub struct PipelineLayout {
1440 pub(crate) raw: ResourceState<Box<dyn hal::DynPipelineLayout>>,
1441 pub(crate) device: Arc<Device>,
1442 pub(crate) label: String,
1444 pub(crate) bind_group_layouts: ArrayVec<Option<Arc<BindGroupLayout>>, { hal::MAX_BIND_GROUPS }>,
1445 pub(crate) immediate_size: u32,
1446 pub(crate) buffers_and_acceleration_structures_in_vertex_stage: u32,
1447}
1448
1449impl Drop for PipelineLayout {
1450 #[allow(trivial_casts)]
1451 fn drop(&mut self) {
1452 profiling::scope!("PipelineLayout::drop");
1453 api_log!("PipelineLayout::drop {:?}", self as *const _);
1454 resource_log!("Destroy raw {}", self.error_ident());
1455 if let ResourceState::Valid(raw) = core::mem::replace(&mut self.raw, ResourceState::Invalid)
1456 {
1457 unsafe {
1458 self.device.raw().destroy_pipeline_layout(raw);
1459 }
1460 }
1461 #[cfg(feature = "trace")]
1462 {
1463 if let Some(t) = self.device.trace.lock().as_mut() {
1464 t.add(crate::device::trace::Action::DropPipelineLayout(unsafe {
1465 crate::device::trace::to_trace(self)
1466 }));
1467 }
1468 }
1469 }
1470}
1471
1472impl PipelineLayout {
1473 pub(crate) fn raw(&self) -> Result<&dyn hal::DynPipelineLayout, InvalidResourceError> {
1474 self.raw
1475 .as_ref()
1476 .valid()
1477 .map(|r| r.as_ref())
1478 .ok_or_else(|| InvalidResourceError(self.error_ident()))
1479 }
1480
1481 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
1482 self.raw
1483 .as_ref()
1484 .valid()
1485 .map(|_| ())
1486 .ok_or_else(|| InvalidResourceError(self.error_ident()))
1487 }
1488
1489 pub(crate) fn get_bind_group_layout(
1490 self: &Arc<Self>,
1491 index: u32,
1492 exclusive_pipeline_for_empty_bgl: ExclusivePipeline,
1493 ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
1494 let max_bind_groups = self.device.limits.max_bind_groups;
1495 if index >= max_bind_groups {
1496 return Err(GetBindGroupLayoutError::IndexOutOfRange {
1497 index,
1498 max: max_bind_groups,
1499 });
1500 }
1501 Ok(self
1502 .bind_group_layouts
1503 .get(index as usize)
1504 .cloned()
1505 .flatten()
1506 .unwrap_or_else(|| {
1507 BindGroupLayout::empty(&self.device, exclusive_pipeline_for_empty_bgl)
1508 }))
1509 }
1510
1511 pub(crate) fn get_bgl_entry(
1512 &self,
1513 group: u32,
1514 binding: u32,
1515 ) -> Option<&wgt::BindGroupLayoutEntry> {
1516 let bgl = self.bind_group_layouts.get(group as usize)?;
1517 let bgl = bgl.as_ref()?;
1518 bgl.entries.get(binding)
1519 }
1520
1521 pub(crate) fn invalid(device: Arc<Device>, label: String) -> Arc<Self> {
1522 Arc::new(Self {
1523 raw: ResourceState::Invalid,
1524 device,
1525 label,
1526 bind_group_layouts: ArrayVec::new(),
1527 immediate_size: 0,
1528 buffers_and_acceleration_structures_in_vertex_stage: 0,
1529 })
1530 }
1531}
1532
1533crate::impl_resource_type!(PipelineLayout);
1534crate::impl_labeled!(PipelineLayout);
1535crate::impl_parent_device!(PipelineLayout);
1536crate::impl_storage_item!(PipelineLayout);
1537
1538#[repr(C)]
1539#[derive(Clone, Debug, Hash, Eq, PartialEq)]
1540#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1541pub struct BufferBinding<B = Arc<Buffer>> {
1542 pub buffer: B,
1543 pub offset: wgt::BufferAddress,
1544
1545 pub size: Option<wgt::BufferAddress>,
1553}
1554
1555#[derive(Debug, Clone)]
1558#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1559pub enum BindingResource<
1560 'a,
1561 B = Arc<Buffer>,
1562 S = Arc<Sampler>,
1563 TV = Arc<TextureView>,
1564 TLAS = Arc<Tlas>,
1565 ET = Arc<ExternalTexture>,
1566> where
1567 [BufferBinding<B>]: ToOwned,
1568 [S]: ToOwned,
1569 [TV]: ToOwned,
1570 [TLAS]: ToOwned,
1571 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
1572 <[S] as ToOwned>::Owned: fmt::Debug,
1573 <[TV] as ToOwned>::Owned: fmt::Debug,
1574 <[TLAS] as ToOwned>::Owned: fmt::Debug,
1575{
1576 Buffer(BufferBinding<B>),
1577 #[cfg_attr(
1578 feature = "serde",
1579 serde(bound(deserialize = "<[BufferBinding<B>] as ToOwned>::Owned: Deserialize<'de>"))
1580 )]
1581 BufferArray(Cow<'a, [BufferBinding<B>]>),
1582 Sampler(S),
1583 #[cfg_attr(
1584 feature = "serde",
1585 serde(bound(deserialize = "<[S] as ToOwned>::Owned: Deserialize<'de>"))
1586 )]
1587 SamplerArray(Cow<'a, [S]>),
1588 TextureView(TV),
1589 #[cfg_attr(
1590 feature = "serde",
1591 serde(bound(deserialize = "<[TV] as ToOwned>::Owned: Deserialize<'de>"))
1592 )]
1593 TextureViewArray(Cow<'a, [TV]>),
1594 AccelerationStructure(TLAS),
1595 #[cfg_attr(
1596 feature = "serde",
1597 serde(bound(deserialize = "<[TLAS] as ToOwned>::Owned: Deserialize<'de>"))
1598 )]
1599 AccelerationStructureArray(Cow<'a, [TLAS]>),
1600 ExternalTexture(ET),
1601}
1602
1603#[derive(Clone, Debug, Error)]
1604#[non_exhaustive]
1605pub enum BindError {
1606 #[error(
1607 "Dynamic offsets not expected with null bind group at index {group}. However {actual} dynamic offset{s1} were provided.",
1608 s1 = if *.actual >= 2 { "s" } else { "" },
1609 )]
1610 DynamicOffsetCountNotZero { group: u32, actual: usize },
1611 #[error(
1612 "{bind_group} at index {group} expects {expected} dynamic offset{s0}, but {actual} dynamic offset{s1} {to_be} provided",
1613 s0 = if *.expected >= 2 { "s" } else { "" },
1614 s1 = if *.actual >= 2 { "s" } else { "" },
1615 to_be = if *.actual == 1 { "was" } else { "were" },
1616 )]
1617 MismatchedDynamicOffsetCount {
1618 bind_group: ResourceErrorIdent,
1619 group: u32,
1620 actual: usize,
1621 expected: usize,
1622 },
1623 #[error(
1624 "Dynamic binding index {idx} (targeting {bind_group} {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}"
1625 )]
1626 UnalignedDynamicBinding {
1627 bind_group: ResourceErrorIdent,
1628 idx: usize,
1629 group: u32,
1630 binding: u32,
1631 offset: u32,
1632 alignment: u32,
1633 limit_name: &'static str,
1634 },
1635 #[error(
1636 "Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to {bind_group} {group} -> binding {binding}. \
1637 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",
1638 )]
1639 DynamicBindingOutOfBounds {
1640 bind_group: ResourceErrorIdent,
1641 idx: usize,
1642 group: u32,
1643 binding: u32,
1644 offset: u32,
1645 buffer_size: wgt::BufferAddress,
1646 binding_range: Range<wgt::BufferAddress>,
1647 maximum_dynamic_offset: wgt::BufferAddress,
1648 },
1649}
1650
1651impl WebGpuError for BindError {
1652 fn webgpu_error_type(&self) -> ErrorType {
1653 ErrorType::Validation
1654 }
1655}
1656
1657#[derive(Debug)]
1658pub struct BindGroupDynamicBindingData {
1659 pub(crate) binding_idx: u32,
1663 pub(crate) buffer_size: wgt::BufferAddress,
1667 pub(crate) binding_range: Range<wgt::BufferAddress>,
1671 pub(crate) maximum_dynamic_offset: wgt::BufferAddress,
1673 pub(crate) binding_type: wgt::BufferBindingType,
1675}
1676
1677pub(crate) fn buffer_binding_type_alignment(
1678 limits: &wgt::Limits,
1679 binding_type: wgt::BufferBindingType,
1680) -> (u32, &'static str) {
1681 match binding_type {
1682 wgt::BufferBindingType::Uniform => (
1683 limits.min_uniform_buffer_offset_alignment,
1684 "min_uniform_buffer_offset_alignment",
1685 ),
1686 wgt::BufferBindingType::Storage { .. } => (
1687 limits.min_storage_buffer_offset_alignment,
1688 "min_storage_buffer_offset_alignment",
1689 ),
1690 }
1691}
1692
1693pub(crate) fn buffer_binding_type_bounds_check_alignment(
1694 alignments: &hal::Alignments,
1695 binding_type: wgt::BufferBindingType,
1696) -> wgt::BufferAddress {
1697 match binding_type {
1698 wgt::BufferBindingType::Uniform => alignments.uniform_bounds_check_alignment.get(),
1699 wgt::BufferBindingType::Storage { .. } => wgt::COPY_BUFFER_ALIGNMENT,
1700 }
1701}
1702
1703#[derive(Debug)]
1704pub(crate) struct BindGroupLateBufferBindingInfo {
1705 pub binding_index: u32,
1707 pub size: wgt::BufferSize,
1709}
1710
1711#[derive(Debug)]
1712pub(crate) struct BindGroupState {
1713 pub(crate) raw: Snatchable<Box<dyn hal::DynBindGroup>>,
1714}
1715
1716#[derive(Debug)]
1717pub struct BindGroup {
1718 pub(crate) state: ResourceState<BindGroupState>,
1719 pub(crate) device: Arc<Device>,
1720 pub(crate) layout: Arc<BindGroupLayout>,
1721 pub(crate) label: String,
1723 pub(crate) tracking_data: TrackingData,
1724 pub(crate) used: BindGroupStates,
1725 pub(crate) buffer_init_actions: Vec<BufferInitTrackerAction>,
1726 pub(crate) texture_init_actions: Vec<TextureInitTrackerAction>,
1727 pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
1729 pub(crate) late_buffer_binding_infos: Vec<BindGroupLateBufferBindingInfo>,
1732}
1733
1734impl Drop for BindGroup {
1735 #[allow(trivial_casts)]
1736 fn drop(&mut self) {
1737 profiling::scope!("BindGroup::drop");
1738 api_log!("BindGroup::drop {:?}", self as *const _);
1739 #[cfg(feature = "trace")]
1740 if let Some(t) = self.device.trace.lock().as_mut() {
1741 use crate::device::trace::{to_trace, Action};
1742 t.add(Action::DropBindGroup(unsafe { to_trace(self) }));
1743 }
1744 let ResourceState::Valid(state) = &mut self.state else {
1745 return;
1746 };
1747 if let Some(raw) = state.raw.take() {
1748 resource_log!("Destroy raw {}", self.error_ident());
1749 unsafe {
1750 self.device.raw().destroy_bind_group(raw);
1751 }
1752 }
1753 }
1754}
1755
1756impl BindGroup {
1757 pub(crate) fn try_raw<'a>(
1758 &'a self,
1759 guard: &'a SnatchGuard,
1760 ) -> Result<&'a dyn hal::DynBindGroup, InvalidOrDestroyedResourceError> {
1761 for buffer in self.used.buffers.used_resources() {
1762 buffer.try_raw(guard)?;
1763 }
1764 for texture in self.used.views.used_textures() {
1765 texture.try_raw(guard)?;
1766 }
1767
1768 self.state()?
1769 .raw
1770 .get(guard)
1771 .map(|raw| raw.as_ref())
1772 .ok_or_else(|| DestroyedResourceError(self.error_ident()).into())
1773 }
1774
1775 pub(crate) fn state(&self) -> Result<&BindGroupState, InvalidResourceError> {
1776 let ResourceState::Valid(state) = &self.state else {
1777 return Err(InvalidResourceError(self.error_ident()));
1778 };
1779 Ok(state)
1780 }
1781
1782 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
1783 self.state().map(|_| ())
1784 }
1785
1786 pub(crate) fn invalid(
1787 device: Arc<Device>,
1788 label: String,
1789 layout: Arc<BindGroupLayout>,
1790 ) -> Arc<Self> {
1791 Arc::new(Self {
1792 state: ResourceState::Invalid,
1793 layout,
1794 label,
1795 tracking_data: TrackingData::new(device.tracker_indices.bind_groups.clone()),
1796 used: BindGroupStates::new(),
1797 buffer_init_actions: Vec::new(),
1798 texture_init_actions: Vec::new(),
1799 dynamic_binding_info: Vec::new(),
1800 late_buffer_binding_infos: Vec::new(),
1801 device,
1802 })
1803 }
1804
1805 pub(crate) fn validate_dynamic_bindings(
1806 &self,
1807 bind_group_index: u32,
1808 offsets: &[wgt::DynamicOffset],
1809 ) -> Result<(), BindError> {
1810 if self.dynamic_binding_info.len() != offsets.len() {
1811 return Err(BindError::MismatchedDynamicOffsetCount {
1812 bind_group: self.error_ident(),
1813 group: bind_group_index,
1814 expected: self.dynamic_binding_info.len(),
1815 actual: offsets.len(),
1816 });
1817 }
1818
1819 for (idx, (info, &offset)) in self
1820 .dynamic_binding_info
1821 .iter()
1822 .zip(offsets.iter())
1823 .enumerate()
1824 {
1825 let (alignment, limit_name) =
1826 buffer_binding_type_alignment(&self.device.limits, info.binding_type);
1827 if !(offset as wgt::BufferAddress).is_multiple_of(alignment as u64) {
1828 return Err(BindError::UnalignedDynamicBinding {
1829 bind_group: self.error_ident(),
1830 group: bind_group_index,
1831 binding: info.binding_idx,
1832 idx,
1833 offset,
1834 alignment,
1835 limit_name,
1836 });
1837 }
1838
1839 if offset as wgt::BufferAddress > info.maximum_dynamic_offset {
1840 return Err(BindError::DynamicBindingOutOfBounds {
1841 bind_group: self.error_ident(),
1842 group: bind_group_index,
1843 binding: info.binding_idx,
1844 idx,
1845 offset,
1846 buffer_size: info.buffer_size,
1847 binding_range: info.binding_range.clone(),
1848 maximum_dynamic_offset: info.maximum_dynamic_offset,
1849 });
1850 }
1851 }
1852
1853 Ok(())
1854 }
1855}
1856
1857crate::impl_resource_type!(BindGroup);
1858crate::impl_labeled!(BindGroup);
1859crate::impl_parent_device!(BindGroup);
1860crate::impl_storage_item!(BindGroup);
1861crate::impl_trackable!(BindGroup);
1862
1863#[derive(Clone, Debug, Error)]
1864#[non_exhaustive]
1865pub enum GetBindGroupLayoutError {
1866 #[error("Bind group layout index {index} is greater than the device's configured `max_bind_groups` limit {max}")]
1867 IndexOutOfRange { index: u32, max: u32 },
1868 #[error(transparent)]
1869 InvalidResource(#[from] InvalidResourceError),
1870}
1871
1872impl WebGpuError for GetBindGroupLayoutError {
1873 fn webgpu_error_type(&self) -> ErrorType {
1874 match self {
1875 Self::IndexOutOfRange { .. } => ErrorType::Validation,
1876 Self::InvalidResource(e) => e.webgpu_error_type(),
1877 }
1878 }
1879}
1880
1881#[derive(Clone, Debug, Error, Eq, PartialEq)]
1882#[error(
1883 "In bind group index {group_index}, the buffer bound at binding index {binding_index} \
1884 is bound with size {bound_size} where the shader expects {shader_size}."
1885)]
1886pub struct LateMinBufferBindingSizeMismatch {
1887 pub group_index: u32,
1888 pub binding_index: u32,
1889 pub shader_size: wgt::BufferAddress,
1890 pub bound_size: wgt::BufferAddress,
1891}