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