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 crate::{
19 device::{
20 bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures, SHADER_STAGE_COUNT,
21 },
22 id::{BindGroupLayoutId, BufferId, SamplerId, TextureViewId, TlasId},
23 init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
24 pipeline::{ComputePipeline, RenderPipeline},
25 resource::{
26 Buffer, DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError,
27 MissingTextureUsageError, ResourceErrorIdent, Sampler, TextureView, Tlas, TrackingData,
28 },
29 resource_log,
30 snatch::{SnatchGuard, Snatchable},
31 track::{BindGroupStates, ResourceUsageCompatibilityError},
32 Label,
33};
34
35#[derive(Clone, Debug, Error)]
36#[non_exhaustive]
37pub enum BindGroupLayoutEntryError {
38 #[error("Cube dimension is not expected for texture storage")]
39 StorageTextureCube,
40 #[error("Read-write and read-only storage textures are not allowed by baseline webgpu, they require the native only feature TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES")]
41 StorageTextureReadWrite,
42 #[error("Atomic storage textures are not allowed by baseline webgpu, they require the native only feature TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES")]
43 StorageTextureAtomic,
44 #[error("Arrays of bindings unsupported for this type of binding")]
45 ArrayUnsupported,
46 #[error("Multisampled binding with sample type `TextureSampleType::Float` must have filterable set to false.")]
47 SampleTypeFloatFilterableBindingMultisampled,
48 #[error("Multisampled texture binding view dimension must be 2d, got {0:?}")]
49 Non2DMultisampled(wgt::TextureViewDimension),
50 #[error(transparent)]
51 MissingFeatures(#[from] MissingFeatures),
52 #[error(transparent)]
53 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
54}
55
56#[derive(Clone, Debug, Error)]
57#[non_exhaustive]
58pub enum CreateBindGroupLayoutError {
59 #[error(transparent)]
60 Device(#[from] DeviceError),
61 #[error("Conflicting binding at index {0}")]
62 ConflictBinding(u32),
63 #[error("Binding {binding} entry is invalid")]
64 Entry {
65 binding: u32,
66 #[source]
67 error: BindGroupLayoutEntryError,
68 },
69 #[error(transparent)]
70 TooManyBindings(BindingTypeMaxCountError),
71 #[error("Bind groups may not contain both a binding array and a dynamically offset buffer")]
72 ContainsBothBindingArrayAndDynamicOffsetArray,
73 #[error("Bind groups may not contain both a binding array and a uniform buffer")]
74 ContainsBothBindingArrayAndUniformBuffer,
75 #[error("Binding index {binding} is greater than the maximum number {maximum}")]
76 InvalidBindingIndex { binding: u32, maximum: u32 },
77 #[error("Invalid visibility {0:?}")]
78 InvalidVisibility(wgt::ShaderStages),
79}
80
81#[derive(Clone, Debug, Error)]
84#[non_exhaustive]
85pub enum CreateBindGroupError {
86 #[error(transparent)]
87 Device(#[from] DeviceError),
88 #[error(transparent)]
89 DestroyedResource(#[from] DestroyedResourceError),
90 #[error(
91 "Binding count declared with at most {expected} items, but {actual} items were provided"
92 )]
93 BindingArrayPartialLengthMismatch { actual: usize, expected: usize },
94 #[error(
95 "Binding count declared with exactly {expected} items, but {actual} items were provided"
96 )]
97 BindingArrayLengthMismatch { actual: usize, expected: usize },
98 #[error("Array binding provided zero elements")]
99 BindingArrayZeroLength,
100 #[error("The bound range {range:?} of {buffer} overflows its size ({size})")]
101 BindingRangeTooLarge {
102 buffer: ResourceErrorIdent,
103 range: Range<wgt::BufferAddress>,
104 size: u64,
105 },
106 #[error("Binding size {actual} of {buffer} is less than minimum {min}")]
107 BindingSizeTooSmall {
108 buffer: ResourceErrorIdent,
109 actual: u64,
110 min: u64,
111 },
112 #[error("{0} binding size is zero")]
113 BindingZeroSize(ResourceErrorIdent),
114 #[error("Number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
115 BindingsNumMismatch { actual: usize, expected: usize },
116 #[error("Binding {0} is used at least twice in the descriptor")]
117 DuplicateBinding(u32),
118 #[error("Unable to find a corresponding declaration for the given binding {0}")]
119 MissingBindingDeclaration(u32),
120 #[error(transparent)]
121 MissingBufferUsage(#[from] MissingBufferUsageError),
122 #[error(transparent)]
123 MissingTextureUsage(#[from] MissingTextureUsageError),
124 #[error("Binding declared as a single item, but bind group is using it as an array")]
125 SingleBindingExpected,
126 #[error("Buffer offset {0} does not respect device's requested `{1}` limit {2}")]
127 UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32),
128 #[error(
129 "Buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
130 )]
131 BufferRangeTooLarge {
132 binding: u32,
133 given: u32,
134 limit: u32,
135 },
136 #[error("Binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
137 WrongBindingType {
138 binding: u32,
140 actual: wgt::BindingType,
142 expected: &'static str,
144 },
145 #[error("Texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}")]
146 InvalidTextureMultisample {
147 binding: u32,
148 layout_multisampled: bool,
149 view_samples: u32,
150 },
151 #[error(
152 "Texture binding {} expects sample type {:?}, but was given a view with format {:?} (sample type {:?})",
153 binding,
154 layout_sample_type,
155 view_format,
156 view_sample_type
157 )]
158 InvalidTextureSampleType {
159 binding: u32,
160 layout_sample_type: wgt::TextureSampleType,
161 view_format: wgt::TextureFormat,
162 view_sample_type: wgt::TextureSampleType,
163 },
164 #[error("Texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}")]
165 InvalidTextureDimension {
166 binding: u32,
167 layout_dimension: wgt::TextureViewDimension,
168 view_dimension: wgt::TextureViewDimension,
169 },
170 #[error("Storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}")]
171 InvalidStorageTextureFormat {
172 binding: u32,
173 layout_format: wgt::TextureFormat,
174 view_format: wgt::TextureFormat,
175 },
176 #[error("Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
177 InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },
178 #[error("Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}")]
179 WrongSamplerComparison {
180 binding: u32,
181 layout_cmp: bool,
182 sampler_cmp: bool,
183 },
184 #[error("Sampler binding {binding} expects filtering = {layout_flt}, but given a sampler with filtering = {sampler_flt}")]
185 WrongSamplerFiltering {
186 binding: u32,
187 layout_flt: bool,
188 sampler_flt: bool,
189 },
190 #[error("TLAS binding {binding} is required to support vertex returns but is missing flag AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN")]
191 MissingTLASVertexReturn { binding: u32 },
192 #[error("Bound texture views can not have both depth and stencil aspects enabled")]
193 DepthStencilAspect,
194 #[error("The adapter does not support read access for storage textures of format {0:?}")]
195 StorageReadNotSupported(wgt::TextureFormat),
196 #[error("The adapter does not support atomics for storage textures of format {0:?}")]
197 StorageAtomicNotSupported(wgt::TextureFormat),
198 #[error("The adapter does not support write access for storage textures of format {0:?}")]
199 StorageWriteNotSupported(wgt::TextureFormat),
200 #[error("The adapter does not support read-write access for storage textures of format {0:?}")]
201 StorageReadWriteNotSupported(wgt::TextureFormat),
202 #[error(transparent)]
203 ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
204 #[error(transparent)]
205 InvalidResource(#[from] InvalidResourceError),
206}
207
208#[derive(Clone, Debug, Error)]
209pub enum BindingZone {
210 #[error("Stage {0:?}")]
211 Stage(wgt::ShaderStages),
212 #[error("Whole pipeline")]
213 Pipeline,
214}
215
216#[derive(Clone, Debug, Error)]
217#[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())]
218pub struct BindingTypeMaxCountError {
219 pub kind: BindingTypeMaxCountErrorKind,
220 pub zone: BindingZone,
221 pub limit: u32,
222 pub count: u32,
223}
224
225#[derive(Clone, Debug)]
226pub enum BindingTypeMaxCountErrorKind {
227 DynamicUniformBuffers,
228 DynamicStorageBuffers,
229 SampledTextures,
230 Samplers,
231 StorageBuffers,
232 StorageTextures,
233 UniformBuffers,
234 BindingArrayElements,
235 BindingArraySamplerElements,
236}
237
238impl BindingTypeMaxCountErrorKind {
239 fn to_config_str(&self) -> &'static str {
240 match self {
241 BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {
242 "max_dynamic_uniform_buffers_per_pipeline_layout"
243 }
244 BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {
245 "max_dynamic_storage_buffers_per_pipeline_layout"
246 }
247 BindingTypeMaxCountErrorKind::SampledTextures => {
248 "max_sampled_textures_per_shader_stage"
249 }
250 BindingTypeMaxCountErrorKind::Samplers => "max_samplers_per_shader_stage",
251 BindingTypeMaxCountErrorKind::StorageBuffers => "max_storage_buffers_per_shader_stage",
252 BindingTypeMaxCountErrorKind::StorageTextures => {
253 "max_storage_textures_per_shader_stage"
254 }
255 BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
256 BindingTypeMaxCountErrorKind::BindingArrayElements => {
257 "max_binding_array_elements_per_shader_stage"
258 }
259 BindingTypeMaxCountErrorKind::BindingArraySamplerElements => {
260 "max_binding_array_elements_per_shader_stage"
261 }
262 }
263 }
264}
265
266#[derive(Debug, Default)]
267pub(crate) struct PerStageBindingTypeCounter {
268 vertex: u32,
269 fragment: u32,
270 compute: u32,
271}
272
273impl PerStageBindingTypeCounter {
274 pub(crate) fn add(&mut self, stage: wgt::ShaderStages, count: u32) {
275 if stage.contains(wgt::ShaderStages::VERTEX) {
276 self.vertex += count;
277 }
278 if stage.contains(wgt::ShaderStages::FRAGMENT) {
279 self.fragment += count;
280 }
281 if stage.contains(wgt::ShaderStages::COMPUTE) {
282 self.compute += count;
283 }
284 }
285
286 pub(crate) fn max(&self) -> (BindingZone, u32) {
287 let max_value = self.vertex.max(self.fragment.max(self.compute));
288 let mut stage = wgt::ShaderStages::NONE;
289 if max_value == self.vertex {
290 stage |= wgt::ShaderStages::VERTEX
291 }
292 if max_value == self.fragment {
293 stage |= wgt::ShaderStages::FRAGMENT
294 }
295 if max_value == self.compute {
296 stage |= wgt::ShaderStages::COMPUTE
297 }
298 (BindingZone::Stage(stage), max_value)
299 }
300
301 pub(crate) fn merge(&mut self, other: &Self) {
302 self.vertex = self.vertex.max(other.vertex);
303 self.fragment = self.fragment.max(other.fragment);
304 self.compute = self.compute.max(other.compute);
305 }
306
307 pub(crate) fn validate(
308 &self,
309 limit: u32,
310 kind: BindingTypeMaxCountErrorKind,
311 ) -> Result<(), BindingTypeMaxCountError> {
312 let (zone, count) = self.max();
313 if limit < count {
314 Err(BindingTypeMaxCountError {
315 kind,
316 zone,
317 limit,
318 count,
319 })
320 } else {
321 Ok(())
322 }
323 }
324}
325
326#[derive(Debug, Default)]
327pub(crate) struct BindingTypeMaxCountValidator {
328 dynamic_uniform_buffers: u32,
329 dynamic_storage_buffers: u32,
330 sampled_textures: PerStageBindingTypeCounter,
331 samplers: PerStageBindingTypeCounter,
332 storage_buffers: PerStageBindingTypeCounter,
333 storage_textures: PerStageBindingTypeCounter,
334 uniform_buffers: PerStageBindingTypeCounter,
335 acceleration_structures: PerStageBindingTypeCounter,
336 binding_array_elements: PerStageBindingTypeCounter,
337 binding_array_sampler_elements: PerStageBindingTypeCounter,
338 has_bindless_array: bool,
339}
340
341impl BindingTypeMaxCountValidator {
342 pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {
343 let count = binding.count.map_or(1, |count| count.get());
344
345 if binding.count.is_some() {
346 self.binding_array_elements.add(binding.visibility, count);
347 self.has_bindless_array = true;
348
349 if let wgt::BindingType::Sampler(_) = binding.ty {
350 self.binding_array_sampler_elements
351 .add(binding.visibility, count);
352 }
353 } else {
354 match binding.ty {
355 wgt::BindingType::Buffer {
356 ty: wgt::BufferBindingType::Uniform,
357 has_dynamic_offset,
358 ..
359 } => {
360 self.uniform_buffers.add(binding.visibility, count);
361 if has_dynamic_offset {
362 self.dynamic_uniform_buffers += count;
363 }
364 }
365 wgt::BindingType::Buffer {
366 ty: wgt::BufferBindingType::Storage { .. },
367 has_dynamic_offset,
368 ..
369 } => {
370 self.storage_buffers.add(binding.visibility, count);
371 if has_dynamic_offset {
372 self.dynamic_storage_buffers += count;
373 }
374 }
375 wgt::BindingType::Sampler { .. } => {
376 self.samplers.add(binding.visibility, count);
377 }
378 wgt::BindingType::Texture { .. } => {
379 self.sampled_textures.add(binding.visibility, count);
380 }
381 wgt::BindingType::StorageTexture { .. } => {
382 self.storage_textures.add(binding.visibility, count);
383 }
384 wgt::BindingType::AccelerationStructure { .. } => {
385 self.acceleration_structures.add(binding.visibility, count);
386 }
387 }
388 }
389 }
390
391 pub(crate) fn merge(&mut self, other: &Self) {
392 self.dynamic_uniform_buffers += other.dynamic_uniform_buffers;
393 self.dynamic_storage_buffers += other.dynamic_storage_buffers;
394 self.sampled_textures.merge(&other.sampled_textures);
395 self.samplers.merge(&other.samplers);
396 self.storage_buffers.merge(&other.storage_buffers);
397 self.storage_textures.merge(&other.storage_textures);
398 self.uniform_buffers.merge(&other.uniform_buffers);
399 self.acceleration_structures
400 .merge(&other.acceleration_structures);
401 self.binding_array_elements
402 .merge(&other.binding_array_elements);
403 self.binding_array_sampler_elements
404 .merge(&other.binding_array_sampler_elements);
405 }
406
407 pub(crate) fn validate(&self, limits: &wgt::Limits) -> Result<(), BindingTypeMaxCountError> {
408 if limits.max_dynamic_uniform_buffers_per_pipeline_layout < self.dynamic_uniform_buffers {
409 return Err(BindingTypeMaxCountError {
410 kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,
411 zone: BindingZone::Pipeline,
412 limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,
413 count: self.dynamic_uniform_buffers,
414 });
415 }
416 if limits.max_dynamic_storage_buffers_per_pipeline_layout < self.dynamic_storage_buffers {
417 return Err(BindingTypeMaxCountError {
418 kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,
419 zone: BindingZone::Pipeline,
420 limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,
421 count: self.dynamic_storage_buffers,
422 });
423 }
424 self.sampled_textures.validate(
425 limits.max_sampled_textures_per_shader_stage,
426 BindingTypeMaxCountErrorKind::SampledTextures,
427 )?;
428 self.samplers.validate(
429 limits.max_samplers_per_shader_stage,
430 BindingTypeMaxCountErrorKind::Samplers,
431 )?;
432 self.storage_buffers.validate(
433 limits.max_storage_buffers_per_shader_stage,
434 BindingTypeMaxCountErrorKind::StorageBuffers,
435 )?;
436 self.storage_textures.validate(
437 limits.max_storage_textures_per_shader_stage,
438 BindingTypeMaxCountErrorKind::StorageTextures,
439 )?;
440 self.uniform_buffers.validate(
441 limits.max_uniform_buffers_per_shader_stage,
442 BindingTypeMaxCountErrorKind::UniformBuffers,
443 )?;
444 self.binding_array_elements.validate(
445 limits.max_binding_array_elements_per_shader_stage,
446 BindingTypeMaxCountErrorKind::BindingArrayElements,
447 )?;
448 self.binding_array_sampler_elements.validate(
449 limits.max_binding_array_sampler_elements_per_shader_stage,
450 BindingTypeMaxCountErrorKind::BindingArraySamplerElements,
451 )?;
452 Ok(())
453 }
454
455 pub(crate) fn validate_binding_arrays(&self) -> Result<(), CreateBindGroupLayoutError> {
460 let has_dynamic_offset_array =
461 self.dynamic_uniform_buffers > 0 || self.dynamic_storage_buffers > 0;
462 let has_uniform_buffer = self.uniform_buffers.max().1 > 0;
463 if self.has_bindless_array && has_dynamic_offset_array {
464 return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndDynamicOffsetArray);
465 }
466 if self.has_bindless_array && has_uniform_buffer {
467 return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndUniformBuffer);
468 }
469 Ok(())
470 }
471}
472
473#[derive(Clone, Debug)]
476#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
477pub struct BindGroupEntry<'a, B = BufferId, S = SamplerId, TV = TextureViewId, TLAS = TlasId>
478where
479 [BufferBinding<B>]: ToOwned,
480 [S]: ToOwned,
481 [TV]: ToOwned,
482 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
483 <[S] as ToOwned>::Owned: fmt::Debug,
484 <[TV] as ToOwned>::Owned: fmt::Debug,
485{
486 pub binding: u32,
489 #[cfg_attr(
490 feature = "serde",
491 serde(bound(deserialize = "BindingResource<'a, B, S, TV, TLAS>: Deserialize<'de>"))
492 )]
493 pub resource: BindingResource<'a, B, S, TV, TLAS>,
495}
496
497pub type ResolvedBindGroupEntry<'a> =
499 BindGroupEntry<'a, Arc<Buffer>, Arc<Sampler>, Arc<TextureView>, Arc<Tlas>>;
500
501#[derive(Clone, Debug)]
503#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
504pub struct BindGroupDescriptor<
505 'a,
506 BGL = BindGroupLayoutId,
507 B = BufferId,
508 S = SamplerId,
509 TV = TextureViewId,
510 TLAS = TlasId,
511> where
512 [BufferBinding<B>]: ToOwned,
513 [S]: ToOwned,
514 [TV]: ToOwned,
515 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
516 <[S] as ToOwned>::Owned: fmt::Debug,
517 <[TV] as ToOwned>::Owned: fmt::Debug,
518 [BindGroupEntry<'a, B, S, TV, TLAS>]: ToOwned,
519 <[BindGroupEntry<'a, B, S, TV, TLAS>] as ToOwned>::Owned: fmt::Debug,
520{
521 pub label: Label<'a>,
525 pub layout: BGL,
527 #[cfg_attr(
528 feature = "serde",
529 serde(bound(
530 deserialize = "<[BindGroupEntry<'a, B, S, TV, TLAS>] as ToOwned>::Owned: Deserialize<'de>"
531 ))
532 )]
533 pub entries: Cow<'a, [BindGroupEntry<'a, B, S, TV, TLAS>]>,
535}
536
537pub type ResolvedBindGroupDescriptor<'a> = BindGroupDescriptor<
539 'a,
540 Arc<BindGroupLayout>,
541 Arc<Buffer>,
542 Arc<Sampler>,
543 Arc<TextureView>,
544 Arc<Tlas>,
545>;
546
547#[derive(Clone, Debug)]
549#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
550pub struct BindGroupLayoutDescriptor<'a> {
551 pub label: Label<'a>,
555 pub entries: Cow<'a, [wgt::BindGroupLayoutEntry]>,
557}
558
559#[derive(Debug)]
563pub(crate) enum ExclusivePipeline {
564 None,
565 Render(Weak<RenderPipeline>),
566 Compute(Weak<ComputePipeline>),
567}
568
569impl fmt::Display for ExclusivePipeline {
570 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571 match self {
572 ExclusivePipeline::None => f.write_str("None"),
573 ExclusivePipeline::Render(p) => {
574 if let Some(p) = p.upgrade() {
575 p.error_ident().fmt(f)
576 } else {
577 f.write_str("RenderPipeline")
578 }
579 }
580 ExclusivePipeline::Compute(p) => {
581 if let Some(p) = p.upgrade() {
582 p.error_ident().fmt(f)
583 } else {
584 f.write_str("ComputePipeline")
585 }
586 }
587 }
588 }
589}
590
591#[derive(Debug)]
593pub struct BindGroupLayout {
594 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>,
595 pub(crate) device: Arc<Device>,
596 pub(crate) entries: bgl::EntryMap,
597 pub(crate) origin: bgl::Origin,
604 pub(crate) exclusive_pipeline: crate::OnceCellOrLock<ExclusivePipeline>,
605 #[allow(unused)]
606 pub(crate) binding_count_validator: BindingTypeMaxCountValidator,
607 pub(crate) label: String,
609}
610
611impl Drop for BindGroupLayout {
612 fn drop(&mut self) {
613 resource_log!("Destroy raw {}", self.error_ident());
614 if matches!(self.origin, bgl::Origin::Pool) {
615 self.device.bgl_pool.remove(&self.entries);
616 }
617 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
619 unsafe {
620 self.device.raw().destroy_bind_group_layout(raw);
621 }
622 }
623}
624
625crate::impl_resource_type!(BindGroupLayout);
626crate::impl_labeled!(BindGroupLayout);
627crate::impl_parent_device!(BindGroupLayout);
628crate::impl_storage_item!(BindGroupLayout);
629
630impl BindGroupLayout {
631 pub(crate) fn raw(&self) -> &dyn hal::DynBindGroupLayout {
632 self.raw.as_ref()
633 }
634}
635
636#[derive(Clone, Debug, Error)]
637#[non_exhaustive]
638pub enum CreatePipelineLayoutError {
639 #[error(transparent)]
640 Device(#[from] DeviceError),
641 #[error(
642 "Push constant at index {index} has range bound {bound} not aligned to {}",
643 wgt::PUSH_CONSTANT_ALIGNMENT
644 )]
645 MisalignedPushConstantRange { index: usize, bound: u32 },
646 #[error(transparent)]
647 MissingFeatures(#[from] MissingFeatures),
648 #[error("Push constant range (index {index}) provides for stage(s) {provided:?} but there exists another range that provides stage(s) {intersected:?}. Each stage may only be provided by one range")]
649 MoreThanOnePushConstantRangePerStage {
650 index: usize,
651 provided: wgt::ShaderStages,
652 intersected: wgt::ShaderStages,
653 },
654 #[error("Push constant at index {index} has range {}..{} which exceeds device push constant size limit 0..{max}", range.start, range.end)]
655 PushConstantRangeTooLarge {
656 index: usize,
657 range: Range<u32>,
658 max: u32,
659 },
660 #[error(transparent)]
661 TooManyBindings(BindingTypeMaxCountError),
662 #[error("Bind group layout count {actual} exceeds device bind group limit {max}")]
663 TooManyGroups { actual: usize, max: usize },
664 #[error(transparent)]
665 InvalidResource(#[from] InvalidResourceError),
666}
667
668#[derive(Clone, Debug, Error)]
669#[non_exhaustive]
670pub enum PushConstantUploadError {
671 #[error("Provided push constant with indices {offset}..{end_offset} overruns matching push constant range at index {idx}, with stage(s) {:?} and indices {:?}", range.stages, range.range)]
672 TooLarge {
673 offset: u32,
674 end_offset: u32,
675 idx: usize,
676 range: wgt::PushConstantRange,
677 },
678 #[error("Provided push constant is for stage(s) {actual:?}, stage with a partial match found at index {idx} with stage(s) {matched:?}, however push constants must be complete matches")]
679 PartialRangeMatch {
680 actual: wgt::ShaderStages,
681 idx: usize,
682 matched: wgt::ShaderStages,
683 },
684 #[error("Provided push constant is for stage(s) {actual:?}, but intersects a push constant range (at index {idx}) with stage(s) {missing:?}. Push constants must provide the stages for all ranges they intersect")]
685 MissingStages {
686 actual: wgt::ShaderStages,
687 idx: usize,
688 missing: wgt::ShaderStages,
689 },
690 #[error("Provided push constant is for stage(s) {actual:?}, however the pipeline layout has no push constant range for the stage(s) {unmatched:?}")]
691 UnmatchedStages {
692 actual: wgt::ShaderStages,
693 unmatched: wgt::ShaderStages,
694 },
695 #[error("Provided push constant offset {0} does not respect `PUSH_CONSTANT_ALIGNMENT`")]
696 Unaligned(u32),
697}
698
699#[derive(Clone, Debug, PartialEq, Eq, Hash)]
703#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
704#[cfg_attr(feature = "serde", serde(bound = "BGL: Serialize"))]
705pub struct PipelineLayoutDescriptor<'a, BGL = BindGroupLayoutId>
706where
707 [BGL]: ToOwned,
708 <[BGL] as ToOwned>::Owned: fmt::Debug,
709{
710 pub label: Label<'a>,
714 #[cfg_attr(
717 feature = "serde",
718 serde(bound(deserialize = "<[BGL] as ToOwned>::Owned: Deserialize<'de>"))
719 )]
720 pub bind_group_layouts: Cow<'a, [BGL]>,
721 pub push_constant_ranges: Cow<'a, [wgt::PushConstantRange]>,
729}
730
731pub type ResolvedPipelineLayoutDescriptor<'a> = PipelineLayoutDescriptor<'a, Arc<BindGroupLayout>>;
733
734#[derive(Debug)]
735pub struct PipelineLayout {
736 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineLayout>>,
737 pub(crate) device: Arc<Device>,
738 pub(crate) label: String,
740 pub(crate) bind_group_layouts: ArrayVec<Arc<BindGroupLayout>, { hal::MAX_BIND_GROUPS }>,
741 pub(crate) push_constant_ranges: ArrayVec<wgt::PushConstantRange, { SHADER_STAGE_COUNT }>,
742}
743
744impl Drop for PipelineLayout {
745 fn drop(&mut self) {
746 resource_log!("Destroy raw {}", self.error_ident());
747 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
749 unsafe {
750 self.device.raw().destroy_pipeline_layout(raw);
751 }
752 }
753}
754
755impl PipelineLayout {
756 pub(crate) fn raw(&self) -> &dyn hal::DynPipelineLayout {
757 self.raw.as_ref()
758 }
759
760 pub(crate) fn get_binding_maps(&self) -> ArrayVec<&bgl::EntryMap, { hal::MAX_BIND_GROUPS }> {
761 self.bind_group_layouts
762 .iter()
763 .map(|bgl| &bgl.entries)
764 .collect()
765 }
766
767 pub(crate) fn validate_push_constant_ranges(
769 &self,
770 stages: wgt::ShaderStages,
771 offset: u32,
772 end_offset: u32,
773 ) -> Result<(), PushConstantUploadError> {
774 if offset % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
779 return Err(PushConstantUploadError::Unaligned(offset));
780 }
781
782 let mut used_stages = wgt::ShaderStages::NONE;
802 for (idx, range) in self.push_constant_ranges.iter().enumerate() {
803 if stages.contains(range.stages) {
805 if !(range.range.start <= offset && end_offset <= range.range.end) {
806 return Err(PushConstantUploadError::TooLarge {
807 offset,
808 end_offset,
809 idx,
810 range: range.clone(),
811 });
812 }
813 used_stages |= range.stages;
814 } else if stages.intersects(range.stages) {
815 return Err(PushConstantUploadError::PartialRangeMatch {
818 actual: stages,
819 idx,
820 matched: range.stages,
821 });
822 }
823
824 if offset < range.range.end && range.range.start < end_offset {
826 if !stages.contains(range.stages) {
828 return Err(PushConstantUploadError::MissingStages {
829 actual: stages,
830 idx,
831 missing: stages,
832 });
833 }
834 }
835 }
836 if used_stages != stages {
837 return Err(PushConstantUploadError::UnmatchedStages {
838 actual: stages,
839 unmatched: stages - used_stages,
840 });
841 }
842 Ok(())
843 }
844}
845
846crate::impl_resource_type!(PipelineLayout);
847crate::impl_labeled!(PipelineLayout);
848crate::impl_parent_device!(PipelineLayout);
849crate::impl_storage_item!(PipelineLayout);
850
851#[repr(C)]
852#[derive(Clone, Debug, Hash, Eq, PartialEq)]
853#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
854pub struct BufferBinding<B = BufferId> {
855 pub buffer: B,
856 pub offset: wgt::BufferAddress,
857 pub size: Option<wgt::BufferSize>,
858}
859
860pub type ResolvedBufferBinding = BufferBinding<Arc<Buffer>>;
861
862#[derive(Debug, Clone)]
865#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
866pub enum BindingResource<'a, B = BufferId, S = SamplerId, TV = TextureViewId, TLAS = TlasId>
867where
868 [BufferBinding<B>]: ToOwned,
869 [S]: ToOwned,
870 [TV]: ToOwned,
871 <[BufferBinding<B>] as ToOwned>::Owned: fmt::Debug,
872 <[S] as ToOwned>::Owned: fmt::Debug,
873 <[TV] as ToOwned>::Owned: fmt::Debug,
874{
875 Buffer(BufferBinding<B>),
876 #[cfg_attr(
877 feature = "serde",
878 serde(bound(deserialize = "<[BufferBinding<B>] as ToOwned>::Owned: Deserialize<'de>"))
879 )]
880 BufferArray(Cow<'a, [BufferBinding<B>]>),
881 Sampler(S),
882 #[cfg_attr(
883 feature = "serde",
884 serde(bound(deserialize = "<[S] as ToOwned>::Owned: Deserialize<'de>"))
885 )]
886 SamplerArray(Cow<'a, [S]>),
887 TextureView(TV),
888 #[cfg_attr(
889 feature = "serde",
890 serde(bound(deserialize = "<[TV] as ToOwned>::Owned: Deserialize<'de>"))
891 )]
892 TextureViewArray(Cow<'a, [TV]>),
893 AccelerationStructure(TLAS),
894}
895
896pub type ResolvedBindingResource<'a> =
897 BindingResource<'a, Arc<Buffer>, Arc<Sampler>, Arc<TextureView>, Arc<Tlas>>;
898
899#[derive(Clone, Debug, Error)]
900#[non_exhaustive]
901pub enum BindError {
902 #[error(
903 "{bind_group} {group} expects {expected} dynamic offset{s0}. However {actual} dynamic offset{s1} were provided.",
904 s0 = if *.expected >= 2 { "s" } else { "" },
905 s1 = if *.actual >= 2 { "s" } else { "" },
906 )]
907 MismatchedDynamicOffsetCount {
908 bind_group: ResourceErrorIdent,
909 group: u32,
910 actual: usize,
911 expected: usize,
912 },
913 #[error(
914 "Dynamic binding index {idx} (targeting {bind_group} {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}"
915 )]
916 UnalignedDynamicBinding {
917 bind_group: ResourceErrorIdent,
918 idx: usize,
919 group: u32,
920 binding: u32,
921 offset: u32,
922 alignment: u32,
923 limit_name: &'static str,
924 },
925 #[error(
926 "Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to {bind_group} {group} -> binding {binding}. \
927 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",
928 )]
929 DynamicBindingOutOfBounds {
930 bind_group: ResourceErrorIdent,
931 idx: usize,
932 group: u32,
933 binding: u32,
934 offset: u32,
935 buffer_size: wgt::BufferAddress,
936 binding_range: Range<wgt::BufferAddress>,
937 maximum_dynamic_offset: wgt::BufferAddress,
938 },
939}
940
941#[derive(Debug)]
942pub struct BindGroupDynamicBindingData {
943 pub(crate) binding_idx: u32,
947 pub(crate) buffer_size: wgt::BufferAddress,
951 pub(crate) binding_range: Range<wgt::BufferAddress>,
955 pub(crate) maximum_dynamic_offset: wgt::BufferAddress,
957 pub(crate) binding_type: wgt::BufferBindingType,
959}
960
961pub(crate) fn buffer_binding_type_alignment(
962 limits: &wgt::Limits,
963 binding_type: wgt::BufferBindingType,
964) -> (u32, &'static str) {
965 match binding_type {
966 wgt::BufferBindingType::Uniform => (
967 limits.min_uniform_buffer_offset_alignment,
968 "min_uniform_buffer_offset_alignment",
969 ),
970 wgt::BufferBindingType::Storage { .. } => (
971 limits.min_storage_buffer_offset_alignment,
972 "min_storage_buffer_offset_alignment",
973 ),
974 }
975}
976
977pub(crate) fn buffer_binding_type_bounds_check_alignment(
978 alignments: &hal::Alignments,
979 binding_type: wgt::BufferBindingType,
980) -> wgt::BufferAddress {
981 match binding_type {
982 wgt::BufferBindingType::Uniform => alignments.uniform_bounds_check_alignment.get(),
983 wgt::BufferBindingType::Storage { .. } => wgt::COPY_BUFFER_ALIGNMENT,
984 }
985}
986
987#[derive(Debug)]
988pub struct BindGroup {
989 pub(crate) raw: Snatchable<Box<dyn hal::DynBindGroup>>,
990 pub(crate) device: Arc<Device>,
991 pub(crate) layout: Arc<BindGroupLayout>,
992 pub(crate) label: String,
994 pub(crate) tracking_data: TrackingData,
995 pub(crate) used: BindGroupStates,
996 pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,
997 pub(crate) used_texture_ranges: Vec<TextureInitTrackerAction>,
998 pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
999 pub(crate) late_buffer_binding_sizes: Vec<wgt::BufferSize>,
1002}
1003
1004impl Drop for BindGroup {
1005 fn drop(&mut self) {
1006 if let Some(raw) = self.raw.take() {
1007 resource_log!("Destroy raw {}", self.error_ident());
1008 unsafe {
1009 self.device.raw().destroy_bind_group(raw);
1010 }
1011 }
1012 }
1013}
1014
1015impl BindGroup {
1016 pub(crate) fn try_raw<'a>(
1017 &'a self,
1018 guard: &'a SnatchGuard,
1019 ) -> Result<&'a dyn hal::DynBindGroup, DestroyedResourceError> {
1020 for buffer in &self.used_buffer_ranges {
1023 buffer.buffer.try_raw(guard)?;
1024 }
1025 for texture in &self.used_texture_ranges {
1026 texture.texture.try_raw(guard)?;
1027 }
1028
1029 self.raw
1030 .get(guard)
1031 .map(|raw| raw.as_ref())
1032 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1033 }
1034
1035 pub(crate) fn validate_dynamic_bindings(
1036 &self,
1037 bind_group_index: u32,
1038 offsets: &[wgt::DynamicOffset],
1039 ) -> Result<(), BindError> {
1040 if self.dynamic_binding_info.len() != offsets.len() {
1041 return Err(BindError::MismatchedDynamicOffsetCount {
1042 bind_group: self.error_ident(),
1043 group: bind_group_index,
1044 expected: self.dynamic_binding_info.len(),
1045 actual: offsets.len(),
1046 });
1047 }
1048
1049 for (idx, (info, &offset)) in self
1050 .dynamic_binding_info
1051 .iter()
1052 .zip(offsets.iter())
1053 .enumerate()
1054 {
1055 let (alignment, limit_name) =
1056 buffer_binding_type_alignment(&self.device.limits, info.binding_type);
1057 if offset as wgt::BufferAddress % alignment as u64 != 0 {
1058 return Err(BindError::UnalignedDynamicBinding {
1059 bind_group: self.error_ident(),
1060 group: bind_group_index,
1061 binding: info.binding_idx,
1062 idx,
1063 offset,
1064 alignment,
1065 limit_name,
1066 });
1067 }
1068
1069 if offset as wgt::BufferAddress > info.maximum_dynamic_offset {
1070 return Err(BindError::DynamicBindingOutOfBounds {
1071 bind_group: self.error_ident(),
1072 group: bind_group_index,
1073 binding: info.binding_idx,
1074 idx,
1075 offset,
1076 buffer_size: info.buffer_size,
1077 binding_range: info.binding_range.clone(),
1078 maximum_dynamic_offset: info.maximum_dynamic_offset,
1079 });
1080 }
1081 }
1082
1083 Ok(())
1084 }
1085}
1086
1087crate::impl_resource_type!(BindGroup);
1088crate::impl_labeled!(BindGroup);
1089crate::impl_parent_device!(BindGroup);
1090crate::impl_storage_item!(BindGroup);
1091crate::impl_trackable!(BindGroup);
1092
1093#[derive(Clone, Debug, Error)]
1094#[non_exhaustive]
1095pub enum GetBindGroupLayoutError {
1096 #[error("Invalid group index {0}")]
1097 InvalidGroupIndex(u32),
1098 #[error(transparent)]
1099 InvalidResource(#[from] InvalidResourceError),
1100}
1101
1102#[derive(Clone, Debug, Error, Eq, PartialEq)]
1103#[error("Buffer is bound with size {bound_size} where the shader expects {shader_size} in group[{group_index}] compact index {compact_index}")]
1104pub struct LateMinBufferBindingSizeMismatch {
1105 pub group_index: u32,
1106 pub compact_index: usize,
1107 pub shader_size: wgt::BufferAddress,
1108 pub bound_size: wgt::BufferAddress,
1109}