1use crate::{
2 device::{
3 bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures, SHADER_STAGE_COUNT,
4 },
5 id::{BindGroupLayoutId, BufferId, SamplerId, TextureViewId},
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 thiserror::Error;
33
34#[derive(Clone, Debug, Error)]
35#[non_exhaustive]
36pub enum BindGroupLayoutEntryError {
37 #[error("Cube dimension is not expected for texture storage")]
38 StorageTextureCube,
39 #[error("Read-write and read-only storage textures are not allowed by webgpu, they require the native only feature TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES")]
40 StorageTextureReadWrite,
41 #[error("Arrays of bindings unsupported for this type of binding")]
42 ArrayUnsupported,
43 #[error("Multisampled binding with sample type `TextureSampleType::Float` must have filterable set to false.")]
44 SampleTypeFloatFilterableBindingMultisampled,
45 #[error("Multisampled texture binding view dimension must be 2d, got {0:?}")]
46 Non2DMultisampled(wgt::TextureViewDimension),
47 #[error(transparent)]
48 MissingFeatures(#[from] MissingFeatures),
49 #[error(transparent)]
50 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
51}
52
53#[derive(Clone, Debug, Error)]
54#[non_exhaustive]
55pub enum CreateBindGroupLayoutError {
56 #[error(transparent)]
57 Device(#[from] DeviceError),
58 #[error("Conflicting binding at index {0}")]
59 ConflictBinding(u32),
60 #[error("Binding {binding} entry is invalid")]
61 Entry {
62 binding: u32,
63 #[source]
64 error: BindGroupLayoutEntryError,
65 },
66 #[error(transparent)]
67 TooManyBindings(BindingTypeMaxCountError),
68 #[error("Binding index {binding} is greater than the maximum number {maximum}")]
69 InvalidBindingIndex { binding: u32, maximum: u32 },
70 #[error("Invalid visibility {0:?}")]
71 InvalidVisibility(wgt::ShaderStages),
72}
73
74#[derive(Clone, Debug, Error)]
77#[non_exhaustive]
78pub enum CreateBindGroupError {
79 #[error(transparent)]
80 Device(#[from] DeviceError),
81 #[error(transparent)]
82 DestroyedResource(#[from] DestroyedResourceError),
83 #[error(
84 "Binding count declared with at most {expected} items, but {actual} items were provided"
85 )]
86 BindingArrayPartialLengthMismatch { actual: usize, expected: usize },
87 #[error(
88 "Binding count declared with exactly {expected} items, but {actual} items were provided"
89 )]
90 BindingArrayLengthMismatch { actual: usize, expected: usize },
91 #[error("Array binding provided zero elements")]
92 BindingArrayZeroLength,
93 #[error("The bound range {range:?} of {buffer} overflows its size ({size})")]
94 BindingRangeTooLarge {
95 buffer: ResourceErrorIdent,
96 range: Range<wgt::BufferAddress>,
97 size: u64,
98 },
99 #[error("Binding size {actual} of {buffer} is less than minimum {min}")]
100 BindingSizeTooSmall {
101 buffer: ResourceErrorIdent,
102 actual: u64,
103 min: u64,
104 },
105 #[error("{0} binding size is zero")]
106 BindingZeroSize(ResourceErrorIdent),
107 #[error("Number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
108 BindingsNumMismatch { actual: usize, expected: usize },
109 #[error("Binding {0} is used at least twice in the descriptor")]
110 DuplicateBinding(u32),
111 #[error("Unable to find a corresponding declaration for the given binding {0}")]
112 MissingBindingDeclaration(u32),
113 #[error(transparent)]
114 MissingBufferUsage(#[from] MissingBufferUsageError),
115 #[error(transparent)]
116 MissingTextureUsage(#[from] MissingTextureUsageError),
117 #[error("Binding declared as a single item, but bind group is using it as an array")]
118 SingleBindingExpected,
119 #[error("Buffer offset {0} does not respect device's requested `{1}` limit {2}")]
120 UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32),
121 #[error(
122 "Buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
123 )]
124 BufferRangeTooLarge {
125 binding: u32,
126 given: u32,
127 limit: u32,
128 },
129 #[error("Binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
130 WrongBindingType {
131 binding: u32,
133 actual: wgt::BindingType,
135 expected: &'static str,
137 },
138 #[error("Texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}")]
139 InvalidTextureMultisample {
140 binding: u32,
141 layout_multisampled: bool,
142 view_samples: u32,
143 },
144 #[error("Texture binding {binding} expects sample type = {layout_sample_type:?}, but given a view with format = {view_format:?}")]
145 InvalidTextureSampleType {
146 binding: u32,
147 layout_sample_type: wgt::TextureSampleType,
148 view_format: wgt::TextureFormat,
149 },
150 #[error("Texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}")]
151 InvalidTextureDimension {
152 binding: u32,
153 layout_dimension: wgt::TextureViewDimension,
154 view_dimension: wgt::TextureViewDimension,
155 },
156 #[error("Storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}")]
157 InvalidStorageTextureFormat {
158 binding: u32,
159 layout_format: wgt::TextureFormat,
160 view_format: wgt::TextureFormat,
161 },
162 #[error("Storage texture bindings must have a single mip level, but given a view with mip_level_count = {mip_level_count:?} at binding {binding}")]
163 InvalidStorageTextureMipLevelCount { binding: u32, mip_level_count: u32 },
164 #[error("Sampler binding {binding} expects comparison = {layout_cmp}, but given a sampler with comparison = {sampler_cmp}")]
165 WrongSamplerComparison {
166 binding: u32,
167 layout_cmp: bool,
168 sampler_cmp: bool,
169 },
170 #[error("Sampler binding {binding} expects filtering = {layout_flt}, but given a sampler with filtering = {sampler_flt}")]
171 WrongSamplerFiltering {
172 binding: u32,
173 layout_flt: bool,
174 sampler_flt: bool,
175 },
176 #[error("Bound texture views can not have both depth and stencil aspects enabled")]
177 DepthStencilAspect,
178 #[error("The adapter does not support read access for storages texture of format {0:?}")]
179 StorageReadNotSupported(wgt::TextureFormat),
180 #[error(transparent)]
181 ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
182 #[error(transparent)]
183 InvalidResource(#[from] InvalidResourceError),
184}
185
186#[derive(Clone, Debug, Error)]
187pub enum BindingZone {
188 #[error("Stage {0:?}")]
189 Stage(wgt::ShaderStages),
190 #[error("Whole pipeline")]
191 Pipeline,
192}
193
194#[derive(Clone, Debug, Error)]
195#[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())]
196pub struct BindingTypeMaxCountError {
197 pub kind: BindingTypeMaxCountErrorKind,
198 pub zone: BindingZone,
199 pub limit: u32,
200 pub count: u32,
201}
202
203#[derive(Clone, Debug)]
204pub enum BindingTypeMaxCountErrorKind {
205 DynamicUniformBuffers,
206 DynamicStorageBuffers,
207 SampledTextures,
208 Samplers,
209 StorageBuffers,
210 StorageTextures,
211 UniformBuffers,
212}
213
214impl BindingTypeMaxCountErrorKind {
215 fn to_config_str(&self) -> &'static str {
216 match self {
217 BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {
218 "max_dynamic_uniform_buffers_per_pipeline_layout"
219 }
220 BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {
221 "max_dynamic_storage_buffers_per_pipeline_layout"
222 }
223 BindingTypeMaxCountErrorKind::SampledTextures => {
224 "max_sampled_textures_per_shader_stage"
225 }
226 BindingTypeMaxCountErrorKind::Samplers => "max_samplers_per_shader_stage",
227 BindingTypeMaxCountErrorKind::StorageBuffers => "max_storage_buffers_per_shader_stage",
228 BindingTypeMaxCountErrorKind::StorageTextures => {
229 "max_storage_textures_per_shader_stage"
230 }
231 BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
232 }
233 }
234}
235
236#[derive(Debug, Default)]
237pub(crate) struct PerStageBindingTypeCounter {
238 vertex: u32,
239 fragment: u32,
240 compute: u32,
241}
242
243impl PerStageBindingTypeCounter {
244 pub(crate) fn add(&mut self, stage: wgt::ShaderStages, count: u32) {
245 if stage.contains(wgt::ShaderStages::VERTEX) {
246 self.vertex += count;
247 }
248 if stage.contains(wgt::ShaderStages::FRAGMENT) {
249 self.fragment += count;
250 }
251 if stage.contains(wgt::ShaderStages::COMPUTE) {
252 self.compute += count;
253 }
254 }
255
256 pub(crate) fn max(&self) -> (BindingZone, u32) {
257 let max_value = self.vertex.max(self.fragment.max(self.compute));
258 let mut stage = wgt::ShaderStages::NONE;
259 if max_value == self.vertex {
260 stage |= wgt::ShaderStages::VERTEX
261 }
262 if max_value == self.fragment {
263 stage |= wgt::ShaderStages::FRAGMENT
264 }
265 if max_value == self.compute {
266 stage |= wgt::ShaderStages::COMPUTE
267 }
268 (BindingZone::Stage(stage), max_value)
269 }
270
271 pub(crate) fn merge(&mut self, other: &Self) {
272 self.vertex = self.vertex.max(other.vertex);
273 self.fragment = self.fragment.max(other.fragment);
274 self.compute = self.compute.max(other.compute);
275 }
276
277 pub(crate) fn validate(
278 &self,
279 limit: u32,
280 kind: BindingTypeMaxCountErrorKind,
281 ) -> Result<(), BindingTypeMaxCountError> {
282 let (zone, count) = self.max();
283 if limit < count {
284 Err(BindingTypeMaxCountError {
285 kind,
286 zone,
287 limit,
288 count,
289 })
290 } else {
291 Ok(())
292 }
293 }
294}
295
296#[derive(Debug, Default)]
297pub(crate) struct BindingTypeMaxCountValidator {
298 dynamic_uniform_buffers: u32,
299 dynamic_storage_buffers: u32,
300 sampled_textures: PerStageBindingTypeCounter,
301 samplers: PerStageBindingTypeCounter,
302 storage_buffers: PerStageBindingTypeCounter,
303 storage_textures: PerStageBindingTypeCounter,
304 uniform_buffers: PerStageBindingTypeCounter,
305}
306
307impl BindingTypeMaxCountValidator {
308 pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {
309 let count = binding.count.map_or(1, |count| count.get());
310 match binding.ty {
311 wgt::BindingType::Buffer {
312 ty: wgt::BufferBindingType::Uniform,
313 has_dynamic_offset,
314 ..
315 } => {
316 self.uniform_buffers.add(binding.visibility, count);
317 if has_dynamic_offset {
318 self.dynamic_uniform_buffers += count;
319 }
320 }
321 wgt::BindingType::Buffer {
322 ty: wgt::BufferBindingType::Storage { .. },
323 has_dynamic_offset,
324 ..
325 } => {
326 self.storage_buffers.add(binding.visibility, count);
327 if has_dynamic_offset {
328 self.dynamic_storage_buffers += count;
329 }
330 }
331 wgt::BindingType::Sampler { .. } => {
332 self.samplers.add(binding.visibility, count);
333 }
334 wgt::BindingType::Texture { .. } => {
335 self.sampled_textures.add(binding.visibility, count);
336 }
337 wgt::BindingType::StorageTexture { .. } => {
338 self.storage_textures.add(binding.visibility, count);
339 }
340 wgt::BindingType::AccelerationStructure => todo!(),
341 }
342 }
343
344 pub(crate) fn merge(&mut self, other: &Self) {
345 self.dynamic_uniform_buffers += other.dynamic_uniform_buffers;
346 self.dynamic_storage_buffers += other.dynamic_storage_buffers;
347 self.sampled_textures.merge(&other.sampled_textures);
348 self.samplers.merge(&other.samplers);
349 self.storage_buffers.merge(&other.storage_buffers);
350 self.storage_textures.merge(&other.storage_textures);
351 self.uniform_buffers.merge(&other.uniform_buffers);
352 }
353
354 pub(crate) fn validate(&self, limits: &wgt::Limits) -> Result<(), BindingTypeMaxCountError> {
355 if limits.max_dynamic_uniform_buffers_per_pipeline_layout < self.dynamic_uniform_buffers {
356 return Err(BindingTypeMaxCountError {
357 kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,
358 zone: BindingZone::Pipeline,
359 limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,
360 count: self.dynamic_uniform_buffers,
361 });
362 }
363 if limits.max_dynamic_storage_buffers_per_pipeline_layout < self.dynamic_storage_buffers {
364 return Err(BindingTypeMaxCountError {
365 kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,
366 zone: BindingZone::Pipeline,
367 limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,
368 count: self.dynamic_storage_buffers,
369 });
370 }
371 self.sampled_textures.validate(
372 limits.max_sampled_textures_per_shader_stage,
373 BindingTypeMaxCountErrorKind::SampledTextures,
374 )?;
375 self.samplers.validate(
376 limits.max_samplers_per_shader_stage,
377 BindingTypeMaxCountErrorKind::Samplers,
378 )?;
379 self.storage_buffers.validate(
380 limits.max_storage_buffers_per_shader_stage,
381 BindingTypeMaxCountErrorKind::StorageBuffers,
382 )?;
383 self.storage_textures.validate(
384 limits.max_storage_textures_per_shader_stage,
385 BindingTypeMaxCountErrorKind::StorageTextures,
386 )?;
387 self.uniform_buffers.validate(
388 limits.max_uniform_buffers_per_shader_stage,
389 BindingTypeMaxCountErrorKind::UniformBuffers,
390 )?;
391 Ok(())
392 }
393}
394
395#[derive(Clone, Debug)]
397#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
398pub struct BindGroupEntry<'a> {
399 pub binding: u32,
402 pub resource: BindingResource<'a>,
404}
405
406#[derive(Clone, Debug)]
408pub struct ResolvedBindGroupEntry<'a> {
409 pub binding: u32,
412 pub resource: ResolvedBindingResource<'a>,
414}
415
416#[derive(Clone, Debug)]
418#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
419pub struct BindGroupDescriptor<'a> {
420 pub label: Label<'a>,
424 pub layout: BindGroupLayoutId,
426 pub entries: Cow<'a, [BindGroupEntry<'a>]>,
428}
429
430#[derive(Clone, Debug)]
432pub struct ResolvedBindGroupDescriptor<'a> {
433 pub label: Label<'a>,
437 pub layout: Arc<BindGroupLayout>,
439 pub entries: Cow<'a, [ResolvedBindGroupEntry<'a>]>,
441}
442
443#[derive(Clone, Debug)]
445#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
446pub struct BindGroupLayoutDescriptor<'a> {
447 pub label: Label<'a>,
451 pub entries: Cow<'a, [wgt::BindGroupLayoutEntry]>,
453}
454
455#[derive(Debug)]
459pub(crate) enum ExclusivePipeline {
460 None,
461 Render(Weak<RenderPipeline>),
462 Compute(Weak<ComputePipeline>),
463}
464
465impl std::fmt::Display for ExclusivePipeline {
466 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
467 match self {
468 ExclusivePipeline::None => f.write_str("None"),
469 ExclusivePipeline::Render(p) => {
470 if let Some(p) = p.upgrade() {
471 p.error_ident().fmt(f)
472 } else {
473 f.write_str("RenderPipeline")
474 }
475 }
476 ExclusivePipeline::Compute(p) => {
477 if let Some(p) = p.upgrade() {
478 p.error_ident().fmt(f)
479 } else {
480 f.write_str("ComputePipeline")
481 }
482 }
483 }
484 }
485}
486
487#[derive(Debug)]
489pub struct BindGroupLayout {
490 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>,
491 pub(crate) device: Arc<Device>,
492 pub(crate) entries: bgl::EntryMap,
493 pub(crate) origin: bgl::Origin,
500 pub(crate) exclusive_pipeline: OnceLock<ExclusivePipeline>,
501 #[allow(unused)]
502 pub(crate) binding_count_validator: BindingTypeMaxCountValidator,
503 pub(crate) label: String,
505}
506
507impl Drop for BindGroupLayout {
508 fn drop(&mut self) {
509 resource_log!("Destroy raw {}", self.error_ident());
510 if matches!(self.origin, bgl::Origin::Pool) {
511 self.device.bgl_pool.remove(&self.entries);
512 }
513 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
515 unsafe {
516 self.device.raw().destroy_bind_group_layout(raw);
517 }
518 }
519}
520
521crate::impl_resource_type!(BindGroupLayout);
522crate::impl_labeled!(BindGroupLayout);
523crate::impl_parent_device!(BindGroupLayout);
524crate::impl_storage_item!(BindGroupLayout);
525
526impl BindGroupLayout {
527 pub(crate) fn raw(&self) -> &dyn hal::DynBindGroupLayout {
528 self.raw.as_ref()
529 }
530}
531
532#[derive(Clone, Debug, Error)]
533#[non_exhaustive]
534pub enum CreatePipelineLayoutError {
535 #[error(transparent)]
536 Device(#[from] DeviceError),
537 #[error(
538 "Push constant at index {index} has range bound {bound} not aligned to {}",
539 wgt::PUSH_CONSTANT_ALIGNMENT
540 )]
541 MisalignedPushConstantRange { index: usize, bound: u32 },
542 #[error(transparent)]
543 MissingFeatures(#[from] MissingFeatures),
544 #[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")]
545 MoreThanOnePushConstantRangePerStage {
546 index: usize,
547 provided: wgt::ShaderStages,
548 intersected: wgt::ShaderStages,
549 },
550 #[error("Push constant at index {index} has range {}..{} which exceeds device push constant size limit 0..{max}", range.start, range.end)]
551 PushConstantRangeTooLarge {
552 index: usize,
553 range: Range<u32>,
554 max: u32,
555 },
556 #[error(transparent)]
557 TooManyBindings(BindingTypeMaxCountError),
558 #[error("Bind group layout count {actual} exceeds device bind group limit {max}")]
559 TooManyGroups { actual: usize, max: usize },
560 #[error(transparent)]
561 InvalidResource(#[from] InvalidResourceError),
562}
563
564#[derive(Clone, Debug, Error)]
565#[non_exhaustive]
566pub enum PushConstantUploadError {
567 #[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)]
568 TooLarge {
569 offset: u32,
570 end_offset: u32,
571 idx: usize,
572 range: wgt::PushConstantRange,
573 },
574 #[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")]
575 PartialRangeMatch {
576 actual: wgt::ShaderStages,
577 idx: usize,
578 matched: wgt::ShaderStages,
579 },
580 #[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")]
581 MissingStages {
582 actual: wgt::ShaderStages,
583 idx: usize,
584 missing: wgt::ShaderStages,
585 },
586 #[error("Provided push constant is for stage(s) {actual:?}, however the pipeline layout has no push constant range for the stage(s) {unmatched:?}")]
587 UnmatchedStages {
588 actual: wgt::ShaderStages,
589 unmatched: wgt::ShaderStages,
590 },
591 #[error("Provided push constant offset {0} does not respect `PUSH_CONSTANT_ALIGNMENT`")]
592 Unaligned(u32),
593}
594
595#[derive(Clone, Debug, PartialEq, Eq, Hash)]
599#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
600pub struct PipelineLayoutDescriptor<'a> {
601 pub label: Label<'a>,
605 pub bind_group_layouts: Cow<'a, [BindGroupLayoutId]>,
608 pub push_constant_ranges: Cow<'a, [wgt::PushConstantRange]>,
616}
617
618#[derive(Debug)]
622pub struct ResolvedPipelineLayoutDescriptor<'a> {
623 pub label: Label<'a>,
627 pub bind_group_layouts: Cow<'a, [Arc<BindGroupLayout>]>,
630 pub push_constant_ranges: Cow<'a, [wgt::PushConstantRange]>,
638}
639
640#[derive(Debug)]
641pub struct PipelineLayout {
642 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineLayout>>,
643 pub(crate) device: Arc<Device>,
644 pub(crate) label: String,
646 pub(crate) bind_group_layouts: ArrayVec<Arc<BindGroupLayout>, { hal::MAX_BIND_GROUPS }>,
647 pub(crate) push_constant_ranges: ArrayVec<wgt::PushConstantRange, { SHADER_STAGE_COUNT }>,
648}
649
650impl Drop for PipelineLayout {
651 fn drop(&mut self) {
652 resource_log!("Destroy raw {}", self.error_ident());
653 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
655 unsafe {
656 self.device.raw().destroy_pipeline_layout(raw);
657 }
658 }
659}
660
661impl PipelineLayout {
662 pub(crate) fn raw(&self) -> &dyn hal::DynPipelineLayout {
663 self.raw.as_ref()
664 }
665
666 pub(crate) fn get_binding_maps(&self) -> ArrayVec<&bgl::EntryMap, { hal::MAX_BIND_GROUPS }> {
667 self.bind_group_layouts
668 .iter()
669 .map(|bgl| &bgl.entries)
670 .collect()
671 }
672
673 pub(crate) fn validate_push_constant_ranges(
675 &self,
676 stages: wgt::ShaderStages,
677 offset: u32,
678 end_offset: u32,
679 ) -> Result<(), PushConstantUploadError> {
680 if offset % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
685 return Err(PushConstantUploadError::Unaligned(offset));
686 }
687
688 let mut used_stages = wgt::ShaderStages::NONE;
708 for (idx, range) in self.push_constant_ranges.iter().enumerate() {
709 if stages.contains(range.stages) {
711 if !(range.range.start <= offset && end_offset <= range.range.end) {
712 return Err(PushConstantUploadError::TooLarge {
713 offset,
714 end_offset,
715 idx,
716 range: range.clone(),
717 });
718 }
719 used_stages |= range.stages;
720 } else if stages.intersects(range.stages) {
721 return Err(PushConstantUploadError::PartialRangeMatch {
724 actual: stages,
725 idx,
726 matched: range.stages,
727 });
728 }
729
730 if offset < range.range.end && range.range.start < end_offset {
732 if !stages.contains(range.stages) {
734 return Err(PushConstantUploadError::MissingStages {
735 actual: stages,
736 idx,
737 missing: stages,
738 });
739 }
740 }
741 }
742 if used_stages != stages {
743 return Err(PushConstantUploadError::UnmatchedStages {
744 actual: stages,
745 unmatched: stages - used_stages,
746 });
747 }
748 Ok(())
749 }
750}
751
752crate::impl_resource_type!(PipelineLayout);
753crate::impl_labeled!(PipelineLayout);
754crate::impl_parent_device!(PipelineLayout);
755crate::impl_storage_item!(PipelineLayout);
756
757#[repr(C)]
758#[derive(Clone, Debug, Hash, Eq, PartialEq)]
759#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
760pub struct BufferBinding {
761 pub buffer_id: BufferId,
762 pub offset: wgt::BufferAddress,
763 pub size: Option<wgt::BufferSize>,
764}
765
766#[derive(Clone, Debug)]
767pub struct ResolvedBufferBinding {
768 pub buffer: Arc<Buffer>,
769 pub offset: wgt::BufferAddress,
770 pub size: Option<wgt::BufferSize>,
771}
772
773#[derive(Debug, Clone)]
776#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
777pub enum BindingResource<'a> {
778 Buffer(BufferBinding),
779 BufferArray(Cow<'a, [BufferBinding]>),
780 Sampler(SamplerId),
781 SamplerArray(Cow<'a, [SamplerId]>),
782 TextureView(TextureViewId),
783 TextureViewArray(Cow<'a, [TextureViewId]>),
784}
785
786#[derive(Debug, Clone)]
789pub enum ResolvedBindingResource<'a> {
790 Buffer(ResolvedBufferBinding),
791 BufferArray(Cow<'a, [ResolvedBufferBinding]>),
792 Sampler(Arc<Sampler>),
793 SamplerArray(Cow<'a, [Arc<Sampler>]>),
794 TextureView(Arc<TextureView>),
795 TextureViewArray(Cow<'a, [Arc<TextureView>]>),
796}
797
798#[derive(Clone, Debug, Error)]
799#[non_exhaustive]
800pub enum BindError {
801 #[error(
802 "{bind_group} {group} expects {expected} dynamic offset{s0}. However {actual} dynamic offset{s1} were provided.",
803 s0 = if *.expected >= 2 { "s" } else { "" },
804 s1 = if *.actual >= 2 { "s" } else { "" },
805 )]
806 MismatchedDynamicOffsetCount {
807 bind_group: ResourceErrorIdent,
808 group: u32,
809 actual: usize,
810 expected: usize,
811 },
812 #[error(
813 "Dynamic binding index {idx} (targeting {bind_group} {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}"
814 )]
815 UnalignedDynamicBinding {
816 bind_group: ResourceErrorIdent,
817 idx: usize,
818 group: u32,
819 binding: u32,
820 offset: u32,
821 alignment: u32,
822 limit_name: &'static str,
823 },
824 #[error(
825 "Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to {bind_group} {group} -> binding {binding}. \
826 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",
827 )]
828 DynamicBindingOutOfBounds {
829 bind_group: ResourceErrorIdent,
830 idx: usize,
831 group: u32,
832 binding: u32,
833 offset: u32,
834 buffer_size: wgt::BufferAddress,
835 binding_range: Range<wgt::BufferAddress>,
836 maximum_dynamic_offset: wgt::BufferAddress,
837 },
838}
839
840#[derive(Debug)]
841pub struct BindGroupDynamicBindingData {
842 pub(crate) binding_idx: u32,
846 pub(crate) buffer_size: wgt::BufferAddress,
850 pub(crate) binding_range: Range<wgt::BufferAddress>,
854 pub(crate) maximum_dynamic_offset: wgt::BufferAddress,
856 pub(crate) binding_type: wgt::BufferBindingType,
858}
859
860pub(crate) fn buffer_binding_type_alignment(
861 limits: &wgt::Limits,
862 binding_type: wgt::BufferBindingType,
863) -> (u32, &'static str) {
864 match binding_type {
865 wgt::BufferBindingType::Uniform => (
866 limits.min_uniform_buffer_offset_alignment,
867 "min_uniform_buffer_offset_alignment",
868 ),
869 wgt::BufferBindingType::Storage { .. } => (
870 limits.min_storage_buffer_offset_alignment,
871 "min_storage_buffer_offset_alignment",
872 ),
873 }
874}
875
876pub(crate) fn buffer_binding_type_bounds_check_alignment(
877 alignments: &hal::Alignments,
878 binding_type: wgt::BufferBindingType,
879) -> wgt::BufferAddress {
880 match binding_type {
881 wgt::BufferBindingType::Uniform => alignments.uniform_bounds_check_alignment.get(),
882 wgt::BufferBindingType::Storage { .. } => wgt::COPY_BUFFER_ALIGNMENT,
883 }
884}
885
886#[derive(Debug)]
887pub struct BindGroup {
888 pub(crate) raw: Snatchable<Box<dyn hal::DynBindGroup>>,
889 pub(crate) device: Arc<Device>,
890 pub(crate) layout: Arc<BindGroupLayout>,
891 pub(crate) label: String,
893 pub(crate) tracking_data: TrackingData,
894 pub(crate) used: BindGroupStates,
895 pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,
896 pub(crate) used_texture_ranges: Vec<TextureInitTrackerAction>,
897 pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
898 pub(crate) late_buffer_binding_sizes: Vec<wgt::BufferSize>,
901}
902
903impl Drop for BindGroup {
904 fn drop(&mut self) {
905 if let Some(raw) = self.raw.take() {
906 resource_log!("Destroy raw {}", self.error_ident());
907 unsafe {
908 self.device.raw().destroy_bind_group(raw);
909 }
910 }
911 }
912}
913
914impl BindGroup {
915 pub(crate) fn try_raw<'a>(
916 &'a self,
917 guard: &'a SnatchGuard,
918 ) -> Result<&dyn hal::DynBindGroup, DestroyedResourceError> {
919 for buffer in &self.used_buffer_ranges {
922 buffer.buffer.try_raw(guard)?;
923 }
924 for texture in &self.used_texture_ranges {
925 texture.texture.try_raw(guard)?;
926 }
927
928 self.raw
929 .get(guard)
930 .map(|raw| raw.as_ref())
931 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
932 }
933
934 pub(crate) fn validate_dynamic_bindings(
935 &self,
936 bind_group_index: u32,
937 offsets: &[wgt::DynamicOffset],
938 ) -> Result<(), BindError> {
939 if self.dynamic_binding_info.len() != offsets.len() {
940 return Err(BindError::MismatchedDynamicOffsetCount {
941 bind_group: self.error_ident(),
942 group: bind_group_index,
943 expected: self.dynamic_binding_info.len(),
944 actual: offsets.len(),
945 });
946 }
947
948 for (idx, (info, &offset)) in self
949 .dynamic_binding_info
950 .iter()
951 .zip(offsets.iter())
952 .enumerate()
953 {
954 let (alignment, limit_name) =
955 buffer_binding_type_alignment(&self.device.limits, info.binding_type);
956 if offset as wgt::BufferAddress % alignment as u64 != 0 {
957 return Err(BindError::UnalignedDynamicBinding {
958 bind_group: self.error_ident(),
959 group: bind_group_index,
960 binding: info.binding_idx,
961 idx,
962 offset,
963 alignment,
964 limit_name,
965 });
966 }
967
968 if offset as wgt::BufferAddress > info.maximum_dynamic_offset {
969 return Err(BindError::DynamicBindingOutOfBounds {
970 bind_group: self.error_ident(),
971 group: bind_group_index,
972 binding: info.binding_idx,
973 idx,
974 offset,
975 buffer_size: info.buffer_size,
976 binding_range: info.binding_range.clone(),
977 maximum_dynamic_offset: info.maximum_dynamic_offset,
978 });
979 }
980 }
981
982 Ok(())
983 }
984}
985
986crate::impl_resource_type!(BindGroup);
987crate::impl_labeled!(BindGroup);
988crate::impl_parent_device!(BindGroup);
989crate::impl_storage_item!(BindGroup);
990crate::impl_trackable!(BindGroup);
991
992#[derive(Clone, Debug, Error)]
993#[non_exhaustive]
994pub enum GetBindGroupLayoutError {
995 #[error("Invalid group index {0}")]
996 InvalidGroupIndex(u32),
997 #[error(transparent)]
998 InvalidResource(#[from] InvalidResourceError),
999}
1000
1001#[derive(Clone, Debug, Error, Eq, PartialEq)]
1002#[error("Buffer is bound with size {bound_size} where the shader expects {shader_size} in group[{group_index}] compact index {compact_index}")]
1003pub struct LateMinBufferBindingSizeMismatch {
1004 pub group_index: u32,
1005 pub compact_index: usize,
1006 pub shader_size: wgt::BufferAddress,
1007 pub bound_size: wgt::BufferAddress,
1008}