1use crate::{
2 device::{
3 bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures, SHADER_STAGE_COUNT,
4 },
5 id::{BindGroupLayoutId, BufferId, SamplerId, TextureViewId, TlasId},
6 init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
7 pipeline::{ComputePipeline, RenderPipeline},
8 resource::{
9 Buffer, DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError,
10 MissingTextureUsageError, ResourceErrorIdent, Sampler, TextureView, TrackingData,
11 },
12 resource_log,
13 snatch::{SnatchGuard, Snatchable},
14 track::{BindGroupStates, ResourceUsageCompatibilityError},
15 Label,
16};
17
18use arrayvec::ArrayVec;
19
20#[cfg(feature = "serde")]
21use serde::Deserialize;
22#[cfg(feature = "serde")]
23use serde::Serialize;
24
25use std::{
26 borrow::Cow,
27 mem::ManuallyDrop,
28 ops::Range,
29 sync::{Arc, OnceLock, Weak},
30};
31
32use crate::resource::Tlas;
33use thiserror::Error;
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("Binding index {binding} is greater than the maximum number {maximum}")]
72 InvalidBindingIndex { binding: u32, maximum: u32 },
73 #[error("Invalid visibility {0:?}")]
74 InvalidVisibility(wgt::ShaderStages),
75}
76
77#[derive(Clone, Debug, Error)]
80#[non_exhaustive]
81pub enum CreateBindGroupError {
82 #[error(transparent)]
83 Device(#[from] DeviceError),
84 #[error(transparent)]
85 DestroyedResource(#[from] DestroyedResourceError),
86 #[error(
87 "Binding count declared with at most {expected} items, but {actual} items were provided"
88 )]
89 BindingArrayPartialLengthMismatch { actual: usize, expected: usize },
90 #[error(
91 "Binding count declared with exactly {expected} items, but {actual} items were provided"
92 )]
93 BindingArrayLengthMismatch { actual: usize, expected: usize },
94 #[error("Array binding provided zero elements")]
95 BindingArrayZeroLength,
96 #[error("The bound range {range:?} of {buffer} overflows its size ({size})")]
97 BindingRangeTooLarge {
98 buffer: ResourceErrorIdent,
99 range: Range<wgt::BufferAddress>,
100 size: u64,
101 },
102 #[error("Binding size {actual} of {buffer} is less than minimum {min}")]
103 BindingSizeTooSmall {
104 buffer: ResourceErrorIdent,
105 actual: u64,
106 min: u64,
107 },
108 #[error("{0} binding size is zero")]
109 BindingZeroSize(ResourceErrorIdent),
110 #[error("Number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
111 BindingsNumMismatch { actual: usize, expected: usize },
112 #[error("Binding {0} is used at least twice in the descriptor")]
113 DuplicateBinding(u32),
114 #[error("Unable to find a corresponding declaration for the given binding {0}")]
115 MissingBindingDeclaration(u32),
116 #[error(transparent)]
117 MissingBufferUsage(#[from] MissingBufferUsageError),
118 #[error(transparent)]
119 MissingTextureUsage(#[from] MissingTextureUsageError),
120 #[error("Binding declared as a single item, but bind group is using it as an array")]
121 SingleBindingExpected,
122 #[error("Buffer offset {0} does not respect device's requested `{1}` limit {2}")]
123 UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32),
124 #[error(
125 "Buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
126 )]
127 BufferRangeTooLarge {
128 binding: u32,
129 given: u32,
130 limit: u32,
131 },
132 #[error("Binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
133 WrongBindingType {
134 binding: u32,
136 actual: wgt::BindingType,
138 expected: &'static str,
140 },
141 #[error("Texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}")]
142 InvalidTextureMultisample {
143 binding: u32,
144 layout_multisampled: bool,
145 view_samples: u32,
146 },
147 #[error(
148 "Texture binding {} expects sample type {:?}, but was given a view with format {:?} (sample type {:?})",
149 binding,
150 layout_sample_type,
151 view_format,
152 view_sample_type
153 )]
154 InvalidTextureSampleType {
155 binding: u32,
156 layout_sample_type: wgt::TextureSampleType,
157 view_format: wgt::TextureFormat,
158 view_sample_type: wgt::TextureSampleType,
159 },
160 #[error("Texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}")]
161 InvalidTextureDimension {
162 binding: u32,
163 layout_dimension: wgt::TextureViewDimension,
164 view_dimension: wgt::TextureViewDimension,
165 },
166 #[error("Storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}")]
167 InvalidStorageTextureFormat {
168 binding: u32,
169 layout_format: wgt::TextureFormat,
170 view_format: wgt::TextureFormat,
171 },
172 #[error("Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
173 InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },
174 #[error("Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}")]
175 WrongSamplerComparison {
176 binding: u32,
177 layout_cmp: bool,
178 sampler_cmp: bool,
179 },
180 #[error("Sampler binding {binding} expects filtering = {layout_flt}, but given a sampler with filtering = {sampler_flt}")]
181 WrongSamplerFiltering {
182 binding: u32,
183 layout_flt: bool,
184 sampler_flt: bool,
185 },
186 #[error("Bound texture views can not have both depth and stencil aspects enabled")]
187 DepthStencilAspect,
188 #[error("The adapter does not support read access for storage textures of format {0:?}")]
189 StorageReadNotSupported(wgt::TextureFormat),
190 #[error("The adapter does not support atomics for storage textures of format {0:?}")]
191 StorageAtomicNotSupported(wgt::TextureFormat),
192 #[error("The adapter does not support write access for storage textures of format {0:?}")]
193 StorageWriteNotSupported(wgt::TextureFormat),
194 #[error("The adapter does not support read-write access for storage textures of format {0:?}")]
195 StorageReadWriteNotSupported(wgt::TextureFormat),
196 #[error(transparent)]
197 ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
198 #[error(transparent)]
199 InvalidResource(#[from] InvalidResourceError),
200}
201
202#[derive(Clone, Debug, Error)]
203pub enum BindingZone {
204 #[error("Stage {0:?}")]
205 Stage(wgt::ShaderStages),
206 #[error("Whole pipeline")]
207 Pipeline,
208}
209
210#[derive(Clone, Debug, Error)]
211#[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())]
212pub struct BindingTypeMaxCountError {
213 pub kind: BindingTypeMaxCountErrorKind,
214 pub zone: BindingZone,
215 pub limit: u32,
216 pub count: u32,
217}
218
219#[derive(Clone, Debug)]
220pub enum BindingTypeMaxCountErrorKind {
221 DynamicUniformBuffers,
222 DynamicStorageBuffers,
223 SampledTextures,
224 Samplers,
225 StorageBuffers,
226 StorageTextures,
227 UniformBuffers,
228}
229
230impl BindingTypeMaxCountErrorKind {
231 fn to_config_str(&self) -> &'static str {
232 match self {
233 BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {
234 "max_dynamic_uniform_buffers_per_pipeline_layout"
235 }
236 BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {
237 "max_dynamic_storage_buffers_per_pipeline_layout"
238 }
239 BindingTypeMaxCountErrorKind::SampledTextures => {
240 "max_sampled_textures_per_shader_stage"
241 }
242 BindingTypeMaxCountErrorKind::Samplers => "max_samplers_per_shader_stage",
243 BindingTypeMaxCountErrorKind::StorageBuffers => "max_storage_buffers_per_shader_stage",
244 BindingTypeMaxCountErrorKind::StorageTextures => {
245 "max_storage_textures_per_shader_stage"
246 }
247 BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
248 }
249 }
250}
251
252#[derive(Debug, Default)]
253pub(crate) struct PerStageBindingTypeCounter {
254 vertex: u32,
255 fragment: u32,
256 compute: u32,
257}
258
259impl PerStageBindingTypeCounter {
260 pub(crate) fn add(&mut self, stage: wgt::ShaderStages, count: u32) {
261 if stage.contains(wgt::ShaderStages::VERTEX) {
262 self.vertex += count;
263 }
264 if stage.contains(wgt::ShaderStages::FRAGMENT) {
265 self.fragment += count;
266 }
267 if stage.contains(wgt::ShaderStages::COMPUTE) {
268 self.compute += count;
269 }
270 }
271
272 pub(crate) fn max(&self) -> (BindingZone, u32) {
273 let max_value = self.vertex.max(self.fragment.max(self.compute));
274 let mut stage = wgt::ShaderStages::NONE;
275 if max_value == self.vertex {
276 stage |= wgt::ShaderStages::VERTEX
277 }
278 if max_value == self.fragment {
279 stage |= wgt::ShaderStages::FRAGMENT
280 }
281 if max_value == self.compute {
282 stage |= wgt::ShaderStages::COMPUTE
283 }
284 (BindingZone::Stage(stage), max_value)
285 }
286
287 pub(crate) fn merge(&mut self, other: &Self) {
288 self.vertex = self.vertex.max(other.vertex);
289 self.fragment = self.fragment.max(other.fragment);
290 self.compute = self.compute.max(other.compute);
291 }
292
293 pub(crate) fn validate(
294 &self,
295 limit: u32,
296 kind: BindingTypeMaxCountErrorKind,
297 ) -> Result<(), BindingTypeMaxCountError> {
298 let (zone, count) = self.max();
299 if limit < count {
300 Err(BindingTypeMaxCountError {
301 kind,
302 zone,
303 limit,
304 count,
305 })
306 } else {
307 Ok(())
308 }
309 }
310}
311
312#[derive(Debug, Default)]
313pub(crate) struct BindingTypeMaxCountValidator {
314 dynamic_uniform_buffers: u32,
315 dynamic_storage_buffers: u32,
316 sampled_textures: PerStageBindingTypeCounter,
317 samplers: PerStageBindingTypeCounter,
318 storage_buffers: PerStageBindingTypeCounter,
319 storage_textures: PerStageBindingTypeCounter,
320 uniform_buffers: PerStageBindingTypeCounter,
321 acceleration_structures: PerStageBindingTypeCounter,
322}
323
324impl BindingTypeMaxCountValidator {
325 pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {
326 let count = binding.count.map_or(1, |count| count.get());
327 match binding.ty {
328 wgt::BindingType::Buffer {
329 ty: wgt::BufferBindingType::Uniform,
330 has_dynamic_offset,
331 ..
332 } => {
333 self.uniform_buffers.add(binding.visibility, count);
334 if has_dynamic_offset {
335 self.dynamic_uniform_buffers += count;
336 }
337 }
338 wgt::BindingType::Buffer {
339 ty: wgt::BufferBindingType::Storage { .. },
340 has_dynamic_offset,
341 ..
342 } => {
343 self.storage_buffers.add(binding.visibility, count);
344 if has_dynamic_offset {
345 self.dynamic_storage_buffers += count;
346 }
347 }
348 wgt::BindingType::Sampler { .. } => {
349 self.samplers.add(binding.visibility, count);
350 }
351 wgt::BindingType::Texture { .. } => {
352 self.sampled_textures.add(binding.visibility, count);
353 }
354 wgt::BindingType::StorageTexture { .. } => {
355 self.storage_textures.add(binding.visibility, count);
356 }
357 wgt::BindingType::AccelerationStructure => {
358 self.acceleration_structures.add(binding.visibility, count);
359 }
360 }
361 }
362
363 pub(crate) fn merge(&mut self, other: &Self) {
364 self.dynamic_uniform_buffers += other.dynamic_uniform_buffers;
365 self.dynamic_storage_buffers += other.dynamic_storage_buffers;
366 self.sampled_textures.merge(&other.sampled_textures);
367 self.samplers.merge(&other.samplers);
368 self.storage_buffers.merge(&other.storage_buffers);
369 self.storage_textures.merge(&other.storage_textures);
370 self.uniform_buffers.merge(&other.uniform_buffers);
371 }
372
373 pub(crate) fn validate(&self, limits: &wgt::Limits) -> Result<(), BindingTypeMaxCountError> {
374 if limits.max_dynamic_uniform_buffers_per_pipeline_layout < self.dynamic_uniform_buffers {
375 return Err(BindingTypeMaxCountError {
376 kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,
377 zone: BindingZone::Pipeline,
378 limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,
379 count: self.dynamic_uniform_buffers,
380 });
381 }
382 if limits.max_dynamic_storage_buffers_per_pipeline_layout < self.dynamic_storage_buffers {
383 return Err(BindingTypeMaxCountError {
384 kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,
385 zone: BindingZone::Pipeline,
386 limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,
387 count: self.dynamic_storage_buffers,
388 });
389 }
390 self.sampled_textures.validate(
391 limits.max_sampled_textures_per_shader_stage,
392 BindingTypeMaxCountErrorKind::SampledTextures,
393 )?;
394 self.samplers.validate(
395 limits.max_samplers_per_shader_stage,
396 BindingTypeMaxCountErrorKind::Samplers,
397 )?;
398 self.storage_buffers.validate(
399 limits.max_storage_buffers_per_shader_stage,
400 BindingTypeMaxCountErrorKind::StorageBuffers,
401 )?;
402 self.storage_textures.validate(
403 limits.max_storage_textures_per_shader_stage,
404 BindingTypeMaxCountErrorKind::StorageTextures,
405 )?;
406 self.uniform_buffers.validate(
407 limits.max_uniform_buffers_per_shader_stage,
408 BindingTypeMaxCountErrorKind::UniformBuffers,
409 )?;
410 Ok(())
411 }
412}
413
414#[derive(Clone, Debug)]
416#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
417pub struct BindGroupEntry<'a> {
418 pub binding: u32,
421 pub resource: BindingResource<'a>,
423}
424
425#[derive(Clone, Debug)]
427pub struct ResolvedBindGroupEntry<'a> {
428 pub binding: u32,
431 pub resource: ResolvedBindingResource<'a>,
433}
434
435#[derive(Clone, Debug)]
437#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
438pub struct BindGroupDescriptor<'a> {
439 pub label: Label<'a>,
443 pub layout: BindGroupLayoutId,
445 pub entries: Cow<'a, [BindGroupEntry<'a>]>,
447}
448
449#[derive(Clone, Debug)]
451pub struct ResolvedBindGroupDescriptor<'a> {
452 pub label: Label<'a>,
456 pub layout: Arc<BindGroupLayout>,
458 pub entries: Cow<'a, [ResolvedBindGroupEntry<'a>]>,
460}
461
462#[derive(Clone, Debug)]
464#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
465pub struct BindGroupLayoutDescriptor<'a> {
466 pub label: Label<'a>,
470 pub entries: Cow<'a, [wgt::BindGroupLayoutEntry]>,
472}
473
474#[derive(Debug)]
478pub(crate) enum ExclusivePipeline {
479 None,
480 Render(Weak<RenderPipeline>),
481 Compute(Weak<ComputePipeline>),
482}
483
484impl std::fmt::Display for ExclusivePipeline {
485 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
486 match self {
487 ExclusivePipeline::None => f.write_str("None"),
488 ExclusivePipeline::Render(p) => {
489 if let Some(p) = p.upgrade() {
490 p.error_ident().fmt(f)
491 } else {
492 f.write_str("RenderPipeline")
493 }
494 }
495 ExclusivePipeline::Compute(p) => {
496 if let Some(p) = p.upgrade() {
497 p.error_ident().fmt(f)
498 } else {
499 f.write_str("ComputePipeline")
500 }
501 }
502 }
503 }
504}
505
506#[derive(Debug)]
508pub struct BindGroupLayout {
509 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>,
510 pub(crate) device: Arc<Device>,
511 pub(crate) entries: bgl::EntryMap,
512 pub(crate) origin: bgl::Origin,
519 pub(crate) exclusive_pipeline: OnceLock<ExclusivePipeline>,
520 #[allow(unused)]
521 pub(crate) binding_count_validator: BindingTypeMaxCountValidator,
522 pub(crate) label: String,
524}
525
526impl Drop for BindGroupLayout {
527 fn drop(&mut self) {
528 resource_log!("Destroy raw {}", self.error_ident());
529 if matches!(self.origin, bgl::Origin::Pool) {
530 self.device.bgl_pool.remove(&self.entries);
531 }
532 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
534 unsafe {
535 self.device.raw().destroy_bind_group_layout(raw);
536 }
537 }
538}
539
540crate::impl_resource_type!(BindGroupLayout);
541crate::impl_labeled!(BindGroupLayout);
542crate::impl_parent_device!(BindGroupLayout);
543crate::impl_storage_item!(BindGroupLayout);
544
545impl BindGroupLayout {
546 pub(crate) fn raw(&self) -> &dyn hal::DynBindGroupLayout {
547 self.raw.as_ref()
548 }
549}
550
551#[derive(Clone, Debug, Error)]
552#[non_exhaustive]
553pub enum CreatePipelineLayoutError {
554 #[error(transparent)]
555 Device(#[from] DeviceError),
556 #[error(
557 "Push constant at index {index} has range bound {bound} not aligned to {}",
558 wgt::PUSH_CONSTANT_ALIGNMENT
559 )]
560 MisalignedPushConstantRange { index: usize, bound: u32 },
561 #[error(transparent)]
562 MissingFeatures(#[from] MissingFeatures),
563 #[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")]
564 MoreThanOnePushConstantRangePerStage {
565 index: usize,
566 provided: wgt::ShaderStages,
567 intersected: wgt::ShaderStages,
568 },
569 #[error("Push constant at index {index} has range {}..{} which exceeds device push constant size limit 0..{max}", range.start, range.end)]
570 PushConstantRangeTooLarge {
571 index: usize,
572 range: Range<u32>,
573 max: u32,
574 },
575 #[error(transparent)]
576 TooManyBindings(BindingTypeMaxCountError),
577 #[error("Bind group layout count {actual} exceeds device bind group limit {max}")]
578 TooManyGroups { actual: usize, max: usize },
579 #[error(transparent)]
580 InvalidResource(#[from] InvalidResourceError),
581}
582
583#[derive(Clone, Debug, Error)]
584#[non_exhaustive]
585pub enum PushConstantUploadError {
586 #[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)]
587 TooLarge {
588 offset: u32,
589 end_offset: u32,
590 idx: usize,
591 range: wgt::PushConstantRange,
592 },
593 #[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")]
594 PartialRangeMatch {
595 actual: wgt::ShaderStages,
596 idx: usize,
597 matched: wgt::ShaderStages,
598 },
599 #[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")]
600 MissingStages {
601 actual: wgt::ShaderStages,
602 idx: usize,
603 missing: wgt::ShaderStages,
604 },
605 #[error("Provided push constant is for stage(s) {actual:?}, however the pipeline layout has no push constant range for the stage(s) {unmatched:?}")]
606 UnmatchedStages {
607 actual: wgt::ShaderStages,
608 unmatched: wgt::ShaderStages,
609 },
610 #[error("Provided push constant offset {0} does not respect `PUSH_CONSTANT_ALIGNMENT`")]
611 Unaligned(u32),
612}
613
614#[derive(Clone, Debug, PartialEq, Eq, Hash)]
618#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
619pub struct PipelineLayoutDescriptor<'a> {
620 pub label: Label<'a>,
624 pub bind_group_layouts: Cow<'a, [BindGroupLayoutId]>,
627 pub push_constant_ranges: Cow<'a, [wgt::PushConstantRange]>,
635}
636
637#[derive(Debug)]
641pub struct ResolvedPipelineLayoutDescriptor<'a> {
642 pub label: Label<'a>,
646 pub bind_group_layouts: Cow<'a, [Arc<BindGroupLayout>]>,
649 pub push_constant_ranges: Cow<'a, [wgt::PushConstantRange]>,
657}
658
659#[derive(Debug)]
660pub struct PipelineLayout {
661 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineLayout>>,
662 pub(crate) device: Arc<Device>,
663 pub(crate) label: String,
665 pub(crate) bind_group_layouts: ArrayVec<Arc<BindGroupLayout>, { hal::MAX_BIND_GROUPS }>,
666 pub(crate) push_constant_ranges: ArrayVec<wgt::PushConstantRange, { SHADER_STAGE_COUNT }>,
667}
668
669impl Drop for PipelineLayout {
670 fn drop(&mut self) {
671 resource_log!("Destroy raw {}", self.error_ident());
672 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
674 unsafe {
675 self.device.raw().destroy_pipeline_layout(raw);
676 }
677 }
678}
679
680impl PipelineLayout {
681 pub(crate) fn raw(&self) -> &dyn hal::DynPipelineLayout {
682 self.raw.as_ref()
683 }
684
685 pub(crate) fn get_binding_maps(&self) -> ArrayVec<&bgl::EntryMap, { hal::MAX_BIND_GROUPS }> {
686 self.bind_group_layouts
687 .iter()
688 .map(|bgl| &bgl.entries)
689 .collect()
690 }
691
692 pub(crate) fn validate_push_constant_ranges(
694 &self,
695 stages: wgt::ShaderStages,
696 offset: u32,
697 end_offset: u32,
698 ) -> Result<(), PushConstantUploadError> {
699 if offset % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
704 return Err(PushConstantUploadError::Unaligned(offset));
705 }
706
707 let mut used_stages = wgt::ShaderStages::NONE;
727 for (idx, range) in self.push_constant_ranges.iter().enumerate() {
728 if stages.contains(range.stages) {
730 if !(range.range.start <= offset && end_offset <= range.range.end) {
731 return Err(PushConstantUploadError::TooLarge {
732 offset,
733 end_offset,
734 idx,
735 range: range.clone(),
736 });
737 }
738 used_stages |= range.stages;
739 } else if stages.intersects(range.stages) {
740 return Err(PushConstantUploadError::PartialRangeMatch {
743 actual: stages,
744 idx,
745 matched: range.stages,
746 });
747 }
748
749 if offset < range.range.end && range.range.start < end_offset {
751 if !stages.contains(range.stages) {
753 return Err(PushConstantUploadError::MissingStages {
754 actual: stages,
755 idx,
756 missing: stages,
757 });
758 }
759 }
760 }
761 if used_stages != stages {
762 return Err(PushConstantUploadError::UnmatchedStages {
763 actual: stages,
764 unmatched: stages - used_stages,
765 });
766 }
767 Ok(())
768 }
769}
770
771crate::impl_resource_type!(PipelineLayout);
772crate::impl_labeled!(PipelineLayout);
773crate::impl_parent_device!(PipelineLayout);
774crate::impl_storage_item!(PipelineLayout);
775
776#[repr(C)]
777#[derive(Clone, Debug, Hash, Eq, PartialEq)]
778#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
779pub struct BufferBinding {
780 pub buffer_id: BufferId,
781 pub offset: wgt::BufferAddress,
782 pub size: Option<wgt::BufferSize>,
783}
784
785#[derive(Clone, Debug)]
786pub struct ResolvedBufferBinding {
787 pub buffer: Arc<Buffer>,
788 pub offset: wgt::BufferAddress,
789 pub size: Option<wgt::BufferSize>,
790}
791
792#[derive(Debug, Clone)]
795#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
796pub enum BindingResource<'a> {
797 Buffer(BufferBinding),
798 BufferArray(Cow<'a, [BufferBinding]>),
799 Sampler(SamplerId),
800 SamplerArray(Cow<'a, [SamplerId]>),
801 TextureView(TextureViewId),
802 TextureViewArray(Cow<'a, [TextureViewId]>),
803 AccelerationStructure(TlasId),
804}
805
806#[derive(Debug, Clone)]
809pub enum ResolvedBindingResource<'a> {
810 Buffer(ResolvedBufferBinding),
811 BufferArray(Cow<'a, [ResolvedBufferBinding]>),
812 Sampler(Arc<Sampler>),
813 SamplerArray(Cow<'a, [Arc<Sampler>]>),
814 TextureView(Arc<TextureView>),
815 TextureViewArray(Cow<'a, [Arc<TextureView>]>),
816 AccelerationStructure(Arc<Tlas>),
817}
818
819#[derive(Clone, Debug, Error)]
820#[non_exhaustive]
821pub enum BindError {
822 #[error(
823 "{bind_group} {group} expects {expected} dynamic offset{s0}. However {actual} dynamic offset{s1} were provided.",
824 s0 = if *.expected >= 2 { "s" } else { "" },
825 s1 = if *.actual >= 2 { "s" } else { "" },
826 )]
827 MismatchedDynamicOffsetCount {
828 bind_group: ResourceErrorIdent,
829 group: u32,
830 actual: usize,
831 expected: usize,
832 },
833 #[error(
834 "Dynamic binding index {idx} (targeting {bind_group} {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}"
835 )]
836 UnalignedDynamicBinding {
837 bind_group: ResourceErrorIdent,
838 idx: usize,
839 group: u32,
840 binding: u32,
841 offset: u32,
842 alignment: u32,
843 limit_name: &'static str,
844 },
845 #[error(
846 "Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to {bind_group} {group} -> binding {binding}. \
847 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",
848 )]
849 DynamicBindingOutOfBounds {
850 bind_group: ResourceErrorIdent,
851 idx: usize,
852 group: u32,
853 binding: u32,
854 offset: u32,
855 buffer_size: wgt::BufferAddress,
856 binding_range: Range<wgt::BufferAddress>,
857 maximum_dynamic_offset: wgt::BufferAddress,
858 },
859}
860
861#[derive(Debug)]
862pub struct BindGroupDynamicBindingData {
863 pub(crate) binding_idx: u32,
867 pub(crate) buffer_size: wgt::BufferAddress,
871 pub(crate) binding_range: Range<wgt::BufferAddress>,
875 pub(crate) maximum_dynamic_offset: wgt::BufferAddress,
877 pub(crate) binding_type: wgt::BufferBindingType,
879}
880
881pub(crate) fn buffer_binding_type_alignment(
882 limits: &wgt::Limits,
883 binding_type: wgt::BufferBindingType,
884) -> (u32, &'static str) {
885 match binding_type {
886 wgt::BufferBindingType::Uniform => (
887 limits.min_uniform_buffer_offset_alignment,
888 "min_uniform_buffer_offset_alignment",
889 ),
890 wgt::BufferBindingType::Storage { .. } => (
891 limits.min_storage_buffer_offset_alignment,
892 "min_storage_buffer_offset_alignment",
893 ),
894 }
895}
896
897pub(crate) fn buffer_binding_type_bounds_check_alignment(
898 alignments: &hal::Alignments,
899 binding_type: wgt::BufferBindingType,
900) -> wgt::BufferAddress {
901 match binding_type {
902 wgt::BufferBindingType::Uniform => alignments.uniform_bounds_check_alignment.get(),
903 wgt::BufferBindingType::Storage { .. } => wgt::COPY_BUFFER_ALIGNMENT,
904 }
905}
906
907#[derive(Debug)]
908pub struct BindGroup {
909 pub(crate) raw: Snatchable<Box<dyn hal::DynBindGroup>>,
910 pub(crate) device: Arc<Device>,
911 pub(crate) layout: Arc<BindGroupLayout>,
912 pub(crate) label: String,
914 pub(crate) tracking_data: TrackingData,
915 pub(crate) used: BindGroupStates,
916 pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,
917 pub(crate) used_texture_ranges: Vec<TextureInitTrackerAction>,
918 pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
919 pub(crate) late_buffer_binding_sizes: Vec<wgt::BufferSize>,
922}
923
924impl Drop for BindGroup {
925 fn drop(&mut self) {
926 if let Some(raw) = self.raw.take() {
927 resource_log!("Destroy raw {}", self.error_ident());
928 unsafe {
929 self.device.raw().destroy_bind_group(raw);
930 }
931 }
932 }
933}
934
935impl BindGroup {
936 pub(crate) fn try_raw<'a>(
937 &'a self,
938 guard: &'a SnatchGuard,
939 ) -> Result<&'a dyn hal::DynBindGroup, DestroyedResourceError> {
940 for buffer in &self.used_buffer_ranges {
943 buffer.buffer.try_raw(guard)?;
944 }
945 for texture in &self.used_texture_ranges {
946 texture.texture.try_raw(guard)?;
947 }
948
949 self.raw
950 .get(guard)
951 .map(|raw| raw.as_ref())
952 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
953 }
954
955 pub(crate) fn validate_dynamic_bindings(
956 &self,
957 bind_group_index: u32,
958 offsets: &[wgt::DynamicOffset],
959 ) -> Result<(), BindError> {
960 if self.dynamic_binding_info.len() != offsets.len() {
961 return Err(BindError::MismatchedDynamicOffsetCount {
962 bind_group: self.error_ident(),
963 group: bind_group_index,
964 expected: self.dynamic_binding_info.len(),
965 actual: offsets.len(),
966 });
967 }
968
969 for (idx, (info, &offset)) in self
970 .dynamic_binding_info
971 .iter()
972 .zip(offsets.iter())
973 .enumerate()
974 {
975 let (alignment, limit_name) =
976 buffer_binding_type_alignment(&self.device.limits, info.binding_type);
977 if offset as wgt::BufferAddress % alignment as u64 != 0 {
978 return Err(BindError::UnalignedDynamicBinding {
979 bind_group: self.error_ident(),
980 group: bind_group_index,
981 binding: info.binding_idx,
982 idx,
983 offset,
984 alignment,
985 limit_name,
986 });
987 }
988
989 if offset as wgt::BufferAddress > info.maximum_dynamic_offset {
990 return Err(BindError::DynamicBindingOutOfBounds {
991 bind_group: self.error_ident(),
992 group: bind_group_index,
993 binding: info.binding_idx,
994 idx,
995 offset,
996 buffer_size: info.buffer_size,
997 binding_range: info.binding_range.clone(),
998 maximum_dynamic_offset: info.maximum_dynamic_offset,
999 });
1000 }
1001 }
1002
1003 Ok(())
1004 }
1005}
1006
1007crate::impl_resource_type!(BindGroup);
1008crate::impl_labeled!(BindGroup);
1009crate::impl_parent_device!(BindGroup);
1010crate::impl_storage_item!(BindGroup);
1011crate::impl_trackable!(BindGroup);
1012
1013#[derive(Clone, Debug, Error)]
1014#[non_exhaustive]
1015pub enum GetBindGroupLayoutError {
1016 #[error("Invalid group index {0}")]
1017 InvalidGroupIndex(u32),
1018 #[error(transparent)]
1019 InvalidResource(#[from] InvalidResourceError),
1020}
1021
1022#[derive(Clone, Debug, Error, Eq, PartialEq)]
1023#[error("Buffer is bound with size {bound_size} where the shader expects {shader_size} in group[{group_index}] compact index {compact_index}")]
1024pub struct LateMinBufferBindingSizeMismatch {
1025 pub group_index: u32,
1026 pub compact_index: usize,
1027 pub shader_size: wgt::BufferAddress,
1028 pub bound_size: wgt::BufferAddress,
1029}