1use super::PipelineShaderStageCreateInfo;
59use crate::{
60 descriptor_set::layout::{
61 DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateFlags,
62 DescriptorSetLayoutCreateInfo, DescriptorType,
63 },
64 device::{Device, DeviceOwned, DeviceOwnedDebugWrapper, DeviceProperties},
65 instance::InstanceOwnedDebugWrapper,
66 macros::{impl_id_counter, vulkan_bitflags},
67 shader::{DescriptorBindingRequirements, ShaderStage, ShaderStages},
68 Validated, ValidationError, VulkanError, VulkanObject,
69};
70use foldhash::HashMap;
71use smallvec::SmallVec;
72use std::{
73 array,
74 cmp::max,
75 collections::hash_map::Entry,
76 error::Error,
77 fmt::{Display, Formatter, Write},
78 mem::MaybeUninit,
79 num::NonZeroU64,
80 ptr,
81 sync::Arc,
82};
83
84#[derive(Debug)]
86pub struct PipelineLayout {
87 handle: ash::vk::PipelineLayout,
88 device: InstanceOwnedDebugWrapper<Arc<Device>>,
89 id: NonZeroU64,
90
91 flags: PipelineLayoutCreateFlags,
92 set_layouts: Vec<DeviceOwnedDebugWrapper<Arc<DescriptorSetLayout>>>,
93 push_constant_ranges: Vec<PushConstantRange>,
94
95 push_constant_ranges_disjoint: Vec<PushConstantRange>,
96}
97
98impl PipelineLayout {
99 pub fn new(
101 device: Arc<Device>,
102 create_info: PipelineLayoutCreateInfo,
103 ) -> Result<Arc<PipelineLayout>, Validated<VulkanError>> {
104 Self::validate_new(&device, &create_info)?;
105
106 Ok(unsafe { Self::new_unchecked(device, create_info) }?)
107 }
108
109 fn validate_new(
110 device: &Device,
111 create_info: &PipelineLayoutCreateInfo,
112 ) -> Result<(), Box<ValidationError>> {
113 create_info
115 .validate(device)
116 .map_err(|err| err.add_context("create_info"))?;
117
118 Ok(())
119 }
120
121 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
122 pub unsafe fn new_unchecked(
123 device: Arc<Device>,
124 create_info: PipelineLayoutCreateInfo,
125 ) -> Result<Arc<PipelineLayout>, VulkanError> {
126 let create_info_fields1_vk = create_info.to_vk_fields1();
127 let create_info_vk = create_info.to_vk(&create_info_fields1_vk);
128
129 let handle = {
130 let fns = device.fns();
131 let mut output = MaybeUninit::uninit();
132 unsafe {
133 (fns.v1_0.create_pipeline_layout)(
134 device.handle(),
135 &create_info_vk,
136 ptr::null(),
137 output.as_mut_ptr(),
138 )
139 }
140 .result()
141 .map_err(VulkanError::from)?;
142 unsafe { output.assume_init() }
143 };
144
145 Ok(unsafe { Self::from_handle(device, handle, create_info) })
146 }
147
148 #[inline]
155 pub unsafe fn from_handle(
156 device: Arc<Device>,
157 handle: ash::vk::PipelineLayout,
158 create_info: PipelineLayoutCreateInfo,
159 ) -> Arc<PipelineLayout> {
160 let PipelineLayoutCreateInfo {
161 flags,
162 set_layouts,
163 mut push_constant_ranges,
164 _ne: _,
165 } = create_info;
166
167 push_constant_ranges.sort_unstable_by_key(|range| {
170 (
171 range.offset,
172 range.size,
173 ash::vk::ShaderStageFlags::from(range.stages),
174 )
175 });
176
177 let mut push_constant_ranges_disjoint: Vec<PushConstantRange> =
178 Vec::with_capacity(push_constant_ranges.len());
179
180 if !push_constant_ranges.is_empty() {
181 let mut min_offset = push_constant_ranges[0].offset;
182 loop {
183 let mut max_offset = u32::MAX;
184 let mut stages = ShaderStages::empty();
185
186 for range in push_constant_ranges.iter() {
187 if range.offset > min_offset {
189 max_offset = max_offset.min(range.offset);
190 break;
191 } else if range.offset + range.size > min_offset {
192 max_offset = max_offset.min(range.offset + range.size);
195 stages |= range.stages;
196 }
197 }
198 if stages.is_empty() {
200 break;
201 }
202
203 push_constant_ranges_disjoint.push(PushConstantRange {
204 stages,
205 offset: min_offset,
206 size: max_offset - min_offset,
207 });
208 min_offset = max_offset;
210 }
211 }
212
213 Arc::new(PipelineLayout {
214 handle,
215 device: InstanceOwnedDebugWrapper(device),
216 id: Self::next_id(),
217 flags,
218 set_layouts: set_layouts
219 .into_iter()
220 .map(DeviceOwnedDebugWrapper)
221 .collect(),
222 push_constant_ranges,
223 push_constant_ranges_disjoint,
224 })
225 }
226
227 #[inline]
229 pub fn flags(&self) -> PipelineLayoutCreateFlags {
230 self.flags
231 }
232
233 #[inline]
235 pub fn set_layouts(&self) -> &[Arc<DescriptorSetLayout>] {
236 DeviceOwnedDebugWrapper::cast_slice_inner(&self.set_layouts)
237 }
238
239 #[inline]
244 pub fn push_constant_ranges(&self) -> &[PushConstantRange] {
245 &self.push_constant_ranges
246 }
247
248 #[doc(hidden)]
261 #[inline]
262 pub fn push_constant_ranges_disjoint(&self) -> &[PushConstantRange] {
263 &self.push_constant_ranges_disjoint
264 }
265
266 #[inline]
268 pub fn is_compatible_with(&self, other: &PipelineLayout, num_sets: u32) -> bool {
269 let num_sets = num_sets as usize;
270 assert!(num_sets <= self.set_layouts.len());
271
272 if self == other {
273 return true;
274 }
275
276 if self.push_constant_ranges != other.push_constant_ranges {
277 return false;
278 }
279
280 let other_sets = match other.set_layouts.get(0..num_sets) {
281 Some(x) => x,
282 None => return false,
283 };
284
285 self.set_layouts
286 .iter()
287 .zip(other_sets)
288 .all(|(self_set_layout, other_set_layout)| {
289 self_set_layout.is_compatible_with(other_set_layout)
290 })
291 }
292
293 pub(crate) fn ensure_compatible_with_shader<'a>(
296 &self,
297 descriptor_requirements: impl IntoIterator<
298 Item = ((u32, u32), &'a DescriptorBindingRequirements),
299 >,
300 push_constant_range: Option<&PushConstantRange>,
301 ) -> Result<(), Box<ValidationError>> {
302 for ((set_num, binding_num), reqs) in descriptor_requirements.into_iter() {
303 let layout_binding = self
304 .set_layouts
305 .get(set_num as usize)
306 .and_then(|set_layout| set_layout.bindings().get(&binding_num));
307
308 let layout_binding = match layout_binding {
309 Some(x) => x,
310 None => {
311 return Err(Box::new(ValidationError {
312 problem: format!(
313 "the requirements for descriptor set {} binding {} were not met: \
314 no such binding exists in the pipeline layout",
315 set_num, binding_num,
316 )
317 .into(),
318 ..Default::default()
319 }));
320 }
321 };
322
323 if let Err(error) = layout_binding.ensure_compatible_with_shader(reqs) {
324 return Err(Box::new(ValidationError {
325 problem: format!(
326 "the requirements for descriptor set {} binding {} were not met: {}",
327 set_num, binding_num, error,
328 )
329 .into(),
330 ..Default::default()
331 }));
332 }
333 }
334
335 if let Some(range) = push_constant_range {
336 for own_range in self.push_constant_ranges.iter() {
337 if range.stages.intersects(own_range.stages) && (range.offset < own_range.offset || own_range.offset + own_range.size < range.offset + range.size)
340 {
341 return Err(Box::new(ValidationError {
342 problem: "the required push constant range is larger than the \
343 push constant range in the pipeline layout"
344 .into(),
345 ..Default::default()
346 }));
347 }
348 }
349 }
350
351 Ok(())
352 }
353}
354
355impl Drop for PipelineLayout {
356 #[inline]
357 fn drop(&mut self) {
358 let fns = self.device.fns();
359 unsafe {
360 (fns.v1_0.destroy_pipeline_layout)(self.device.handle(), self.handle, ptr::null())
361 };
362 }
363}
364
365unsafe impl VulkanObject for PipelineLayout {
366 type Handle = ash::vk::PipelineLayout;
367
368 #[inline]
369 fn handle(&self) -> Self::Handle {
370 self.handle
371 }
372}
373
374unsafe impl DeviceOwned for PipelineLayout {
375 #[inline]
376 fn device(&self) -> &Arc<Device> {
377 &self.device
378 }
379}
380
381impl_id_counter!(PipelineLayout);
382
383#[derive(Clone, Debug)]
385pub struct PipelineLayoutCreateInfo {
386 pub flags: PipelineLayoutCreateFlags,
390
391 pub set_layouts: Vec<Arc<DescriptorSetLayout>>,
397
398 pub push_constant_ranges: Vec<PushConstantRange>,
405
406 pub _ne: crate::NonExhaustive,
407}
408
409impl Default for PipelineLayoutCreateInfo {
410 #[inline]
411 fn default() -> Self {
412 Self {
413 flags: PipelineLayoutCreateFlags::empty(),
414 set_layouts: Vec::new(),
415 push_constant_ranges: Vec::new(),
416 _ne: crate::NonExhaustive(()),
417 }
418 }
419}
420
421impl PipelineLayoutCreateInfo {
422 pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
423 let properties = device.physical_device().properties();
424
425 let &Self {
426 flags,
427 ref set_layouts,
428 ref push_constant_ranges,
429 _ne: _,
430 } = self;
431
432 flags.validate_device(device).map_err(|err| {
433 err.add_context("flags")
434 .set_vuids(&["VUID-VkPipelineLayoutCreateInfo-flags-parameter"])
435 })?;
436
437 if set_layouts.len() > properties.max_bound_descriptor_sets as usize {
438 return Err(Box::new(ValidationError {
439 context: "set_layouts".into(),
440 problem: "the length exceeds the `max_bound_descriptor_sets` limit".into(),
441 vuids: &["VUID-VkPipelineLayoutCreateInfo-setLayoutCount-00286"],
442 ..Default::default()
443 }));
444 }
445
446 struct DescriptorLimit {
447 descriptor_types: &'static [DescriptorType],
448 get_limit_all: fn(&DeviceProperties) -> Option<u32>,
449 limit_name_all: &'static str,
450 vuids_all: &'static [&'static str],
451 get_limit_not_uab: fn(&DeviceProperties) -> u32,
452 limit_name_not_uab: &'static str,
453 vuids_not_uab: &'static [&'static str],
454 }
455
456 const PER_STAGE_DESCRIPTOR_LIMITS: [DescriptorLimit; 8] = [
457 DescriptorLimit {
458 descriptor_types: &[
459 DescriptorType::Sampler,
460 DescriptorType::CombinedImageSampler,
461 ],
462 get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_samplers,
463 limit_name_all: "max_per_stage_descriptor_update_after_bind_samplers",
464 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03022"],
465 get_limit_not_uab: |p| p.max_per_stage_descriptor_samplers,
466 limit_name_not_uab: "max_per_stage_descriptor_samplers",
467 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03016"],
468 },
469 DescriptorLimit {
470 descriptor_types: &[
471 DescriptorType::UniformBuffer,
472 DescriptorType::UniformBufferDynamic,
473 ],
474 get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_uniform_buffers,
475 limit_name_all: "max_per_stage_descriptor_update_after_bind_uniform_buffers",
476 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03023"],
477 get_limit_not_uab: |p| p.max_per_stage_descriptor_uniform_buffers,
478 limit_name_not_uab: "max_per_stage_descriptor_uniform_buffers",
479 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03017"],
480 },
481 DescriptorLimit {
482 descriptor_types: &[
483 DescriptorType::StorageBuffer,
484 DescriptorType::StorageBufferDynamic,
485 ],
486 get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_storage_buffers,
487 limit_name_all: "max_per_stage_descriptor_update_after_bind_storage_buffers",
488 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03024"],
489 get_limit_not_uab: |p| p.max_per_stage_descriptor_storage_buffers,
490 limit_name_not_uab: "max_per_stage_descriptor_storage_buffers",
491 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03018"],
492 },
493 DescriptorLimit {
494 descriptor_types: &[
495 DescriptorType::CombinedImageSampler,
496 DescriptorType::SampledImage,
497 DescriptorType::UniformTexelBuffer,
498 ],
499 get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_sampled_images,
500 limit_name_all: "max_per_stage_descriptor_update_after_bind_sampled_images",
501 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03025"],
502 get_limit_not_uab: |p| p.max_per_stage_descriptor_sampled_images,
503 limit_name_not_uab: "max_per_stage_descriptor_sampled_images",
504 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-06939"],
505 },
506 DescriptorLimit {
507 descriptor_types: &[
508 DescriptorType::StorageImage,
509 DescriptorType::StorageTexelBuffer,
510 ],
511 get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_storage_images,
512 limit_name_all: "max_per_stage_descriptor_update_after_bind_storage_images",
513 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03026"],
514 get_limit_not_uab: |p| p.max_per_stage_descriptor_storage_images,
515 limit_name_not_uab: "max_per_stage_descriptor_storage_images",
516 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03020"],
517 },
518 DescriptorLimit {
519 descriptor_types: &[DescriptorType::InputAttachment],
520 get_limit_all: |p| p.max_per_stage_descriptor_update_after_bind_input_attachments,
521 limit_name_all: "max_per_stage_descriptor_update_after_bind_input_attachments",
522 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03027"],
523 get_limit_not_uab: |p| p.max_per_stage_descriptor_input_attachments,
524 limit_name_not_uab: "max_per_stage_descriptor_input_attachments",
525 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03021"],
526 },
527 DescriptorLimit {
528 descriptor_types: &[DescriptorType::InlineUniformBlock],
529 get_limit_all: |p| {
530 p.max_per_stage_descriptor_update_after_bind_inline_uniform_blocks
531 },
532 limit_name_all: "max_per_stage_descriptor_update_after_bind_inline_uniform_blocks",
533 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-02215"],
534 get_limit_not_uab: |p| {
535 p.max_per_stage_descriptor_inline_uniform_blocks
536 .unwrap_or(0)
537 },
538 limit_name_not_uab: "max_per_stage_descriptor_inline_uniform_blocks",
539 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-02214"],
540 },
541 DescriptorLimit {
542 descriptor_types: &[DescriptorType::AccelerationStructure],
543 get_limit_all: |p| {
544 p.max_per_stage_descriptor_update_after_bind_acceleration_structures
545 },
546 limit_name_all:
547 "max_per_stage_descriptor_update_after_bind_acceleration_structures",
548 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03572"],
549 get_limit_not_uab: |p| {
550 p.max_per_stage_descriptor_acceleration_structures
551 .unwrap_or(0)
552 },
553 limit_name_not_uab: "max_per_stage_descriptor_acceleration_structures",
554 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03571"],
555 },
556 ];
557
558 const TOTAL_DESCRIPTOR_LIMITS: [DescriptorLimit; 10] = [
559 DescriptorLimit {
560 descriptor_types: &[
561 DescriptorType::Sampler,
562 DescriptorType::CombinedImageSampler,
563 ],
564 get_limit_all: |p| p.max_descriptor_set_update_after_bind_samplers,
565 limit_name_all: "max_descriptor_set_update_after_bind_samplers",
566 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03036"],
567 get_limit_not_uab: |p| p.max_descriptor_set_samplers,
568 limit_name_not_uab: "max_descriptor_set_samplers",
569 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03028"],
570 },
571 DescriptorLimit {
572 descriptor_types: &[DescriptorType::UniformBuffer],
573 get_limit_all: |p| p.max_descriptor_set_update_after_bind_uniform_buffers,
574 limit_name_all: "max_descriptor_set_update_after_bind_uniform_buffers",
575 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03037"],
576 get_limit_not_uab: |p| p.max_descriptor_set_uniform_buffers,
577 limit_name_not_uab: "max_descriptor_set_uniform_buffers",
578 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03029"],
579 },
580 DescriptorLimit {
581 descriptor_types: &[DescriptorType::UniformBufferDynamic],
582 get_limit_all: |p| p.max_descriptor_set_update_after_bind_uniform_buffers_dynamic,
583 limit_name_all: "max_descriptor_set_update_after_bind_uniform_buffers_dynamic",
584 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03038"],
585 get_limit_not_uab: |p| p.max_descriptor_set_uniform_buffers_dynamic,
586 limit_name_not_uab: "max_descriptor_set_uniform_buffers_dynamic",
587 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03030"],
588 },
589 DescriptorLimit {
590 descriptor_types: &[DescriptorType::StorageBuffer],
591 get_limit_all: |p| p.max_descriptor_set_update_after_bind_storage_buffers,
592 limit_name_all: "max_descriptor_set_update_after_bind_storage_buffers",
593 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03039"],
594 get_limit_not_uab: |p| p.max_descriptor_set_storage_buffers,
595 limit_name_not_uab: "max_descriptor_set_storage_buffers",
596 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03031"],
597 },
598 DescriptorLimit {
599 descriptor_types: &[DescriptorType::StorageBufferDynamic],
600 get_limit_all: |p| p.max_descriptor_set_update_after_bind_storage_buffers_dynamic,
601 limit_name_all: "max_descriptor_set_update_after_bind_storage_buffers_dynamic",
602 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03040"],
603 get_limit_not_uab: |p| p.max_descriptor_set_storage_buffers_dynamic,
604 limit_name_not_uab: "max_descriptor_set_storage_buffers_dynamic",
605 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03032"],
606 },
607 DescriptorLimit {
608 descriptor_types: &[
609 DescriptorType::CombinedImageSampler,
610 DescriptorType::SampledImage,
611 DescriptorType::UniformTexelBuffer,
612 ],
613 get_limit_all: |p| p.max_descriptor_set_update_after_bind_sampled_images,
614 limit_name_all: "max_descriptor_set_update_after_bind_sampled_images",
615 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03041"],
616 get_limit_not_uab: |p| p.max_descriptor_set_sampled_images,
617 limit_name_not_uab: "max_descriptor_set_sampled_images",
618 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03033"],
619 },
620 DescriptorLimit {
621 descriptor_types: &[
622 DescriptorType::StorageImage,
623 DescriptorType::StorageTexelBuffer,
624 ],
625 get_limit_all: |p| p.max_descriptor_set_update_after_bind_storage_images,
626 limit_name_all: "max_descriptor_set_update_after_bind_storage_images",
627 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03042"],
628 get_limit_not_uab: |p| p.max_descriptor_set_storage_images,
629 limit_name_not_uab: "max_descriptor_set_storage_images",
630 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03034"],
631 },
632 DescriptorLimit {
633 descriptor_types: &[DescriptorType::InputAttachment],
634 get_limit_all: |p| p.max_descriptor_set_update_after_bind_input_attachments,
635 limit_name_all: "max_descriptor_set_update_after_bind_input_attachments",
636 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03043"],
637 get_limit_not_uab: |p| p.max_descriptor_set_input_attachments,
638 limit_name_not_uab: "max_descriptor_set_input_attachments",
639 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03035"],
640 },
641 DescriptorLimit {
642 descriptor_types: &[DescriptorType::InlineUniformBlock],
643 get_limit_all: |p| p.max_descriptor_set_update_after_bind_inline_uniform_blocks,
644 limit_name_all: "max_descriptor_set_update_after_bind_inline_uniform_blocks",
645 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-02217"],
646 get_limit_not_uab: |p| p.max_descriptor_set_inline_uniform_blocks.unwrap_or(0),
647 limit_name_not_uab: "max_descriptor_set_inline_uniform_blocks",
648 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-02216"],
649 },
650 DescriptorLimit {
651 descriptor_types: &[DescriptorType::AccelerationStructure],
652 get_limit_all: |p| p.max_descriptor_set_update_after_bind_acceleration_structures,
653 limit_name_all: "max_descriptor_set_update_after_bind_acceleration_structures",
654 vuids_all: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03574"],
655 get_limit_not_uab: |p| p.max_descriptor_set_acceleration_structures.unwrap_or(0),
656 limit_name_not_uab: "max_descriptor_set_acceleration_structures",
657 vuids_not_uab: &["VUID-VkPipelineLayoutCreateInfo-descriptorType-03573"],
658 },
659 ];
660
661 let mut per_stage_descriptors_all: [HashMap<ShaderStage, u32>;
662 PER_STAGE_DESCRIPTOR_LIMITS.len()] = array::from_fn(|_| HashMap::default());
663 let mut per_stage_descriptors_not_uab: [HashMap<ShaderStage, u32>;
664 PER_STAGE_DESCRIPTOR_LIMITS.len()] = array::from_fn(|_| HashMap::default());
665 let mut total_descriptors_all = [0; TOTAL_DESCRIPTOR_LIMITS.len()];
666 let mut total_descriptors_not_uab = [0; TOTAL_DESCRIPTOR_LIMITS.len()];
667 let mut has_push_descriptor_set = false;
668
669 for set_layout in set_layouts {
670 assert_eq!(device, set_layout.device().as_ref());
671
672 if set_layout
673 .flags()
674 .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR)
675 {
676 if has_push_descriptor_set {
677 return Err(Box::new(ValidationError {
678 context: "set_layouts".into(),
679 problem: "contains more than one descriptor set layout whose flags \
680 include `DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR`"
681 .into(),
682 vuids: &["VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00293"],
683 ..Default::default()
684 }));
685 }
686
687 has_push_descriptor_set = true;
688 }
689
690 let is_not_uab = !set_layout
691 .flags()
692 .intersects(DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL);
693
694 for layout_binding in set_layout.bindings().values() {
695 let &DescriptorSetLayoutBinding {
696 binding_flags: _,
697 descriptor_type,
698 descriptor_count,
699 stages,
700 immutable_samplers: _,
701 _ne: _,
702 } = layout_binding;
703
704 for ((limit, count_all), count_not_uab) in PER_STAGE_DESCRIPTOR_LIMITS
705 .iter()
706 .zip(&mut per_stage_descriptors_all)
707 .zip(&mut per_stage_descriptors_not_uab)
708 {
709 if limit.descriptor_types.contains(&descriptor_type) {
710 for stage in stages {
711 *count_all.entry(stage).or_default() += descriptor_count;
712
713 if is_not_uab {
714 *count_not_uab.entry(stage).or_default() += descriptor_count;
715 }
716 }
717 }
718 }
719
720 for ((limit, count_all), count_not_uab) in TOTAL_DESCRIPTOR_LIMITS
721 .iter()
722 .zip(&mut total_descriptors_all)
723 .zip(&mut total_descriptors_not_uab)
724 {
725 if limit.descriptor_types.contains(&descriptor_type) {
726 *count_all += descriptor_count;
727
728 if is_not_uab {
729 *count_not_uab += descriptor_count;
730 }
731 }
732 }
733 }
734 }
735
736 for ((limit, count_all), count_not_uab) in PER_STAGE_DESCRIPTOR_LIMITS
737 .iter()
738 .zip(per_stage_descriptors_all)
739 .zip(per_stage_descriptors_not_uab)
740 {
741 let limit_not_uab = (limit.get_limit_not_uab)(properties);
742
743 if let Some((max_stage, max_count)) = count_not_uab.into_iter().max_by_key(|(_, c)| *c)
744 {
745 if max_count > limit_not_uab {
746 return Err(Box::new(ValidationError {
747 context: "set_layouts".into(),
748 problem: format!(
749 "the combined number of {} descriptors, \
750 belonging to descriptor set layouts without the \
751 `DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL` flag, \
752 accessible to the `ShaderStage::{:?}` stage, \
753 exceeds the `{}` limit",
754 limit.descriptor_types[1..].iter().fold(
755 format!("`DescriptorType::{:?}`", limit.descriptor_types[0]),
756 |mut s, dt| {
757 write!(s, " + `DescriptorType::{:?}`", dt).unwrap();
758 s
759 }
760 ),
761 max_stage,
762 limit.limit_name_not_uab,
763 )
764 .into(),
765 vuids: limit.vuids_not_uab,
766 ..Default::default()
767 }));
768 }
769 }
770
771 let limit_all = match (limit.get_limit_all)(properties) {
772 Some(x) => x,
773 None => continue,
774 };
775
776 if let Some((max_stage, max_count)) = count_all.into_iter().max_by_key(|(_, c)| *c) {
777 if max_count > limit_all {
778 return Err(Box::new(ValidationError {
779 context: "set_layouts".into(),
780 problem: format!(
781 "the combined number of {} descriptors, \
782 accessible to the `ShaderStage::{:?}` stage, \
783 exceeds the `{}` limit",
784 limit.descriptor_types[1..].iter().fold(
785 format!("`DescriptorType::{:?}`", limit.descriptor_types[0]),
786 |mut s, dt| {
787 write!(s, " + `DescriptorType::{:?}`", dt).unwrap();
788 s
789 }
790 ),
791 max_stage,
792 limit.limit_name_all,
793 )
794 .into(),
795 vuids: limit.vuids_all,
796 ..Default::default()
797 }));
798 }
799 }
800 }
801
802 for ((limit, count_all), count_not_uab) in TOTAL_DESCRIPTOR_LIMITS
803 .iter()
804 .zip(total_descriptors_all)
805 .zip(total_descriptors_not_uab)
806 {
807 let limit_not_uab = (limit.get_limit_not_uab)(properties);
808
809 if count_not_uab > limit_not_uab {
810 return Err(Box::new(ValidationError {
811 context: "set_layouts".into(),
812 problem: format!(
813 "the combined number of {} descriptors, \
814 belonging to descriptor set layouts without the \
815 `DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL` flag, \
816 accessible across all shader stages, \
817 exceeds the `{}` limit",
818 limit.descriptor_types[1..].iter().fold(
819 format!("`DescriptorType::{:?}`", limit.descriptor_types[0]),
820 |mut s, dt| {
821 write!(s, " + `DescriptorType::{:?}`", dt).unwrap();
822 s
823 }
824 ),
825 limit.limit_name_not_uab,
826 )
827 .into(),
828 vuids: limit.vuids_not_uab,
829 ..Default::default()
830 }));
831 }
832
833 let limit_all = match (limit.get_limit_all)(properties) {
834 Some(x) => x,
835 None => continue,
836 };
837
838 if count_all > limit_all {
839 return Err(Box::new(ValidationError {
840 context: "set_layouts".into(),
841 problem: format!(
842 "the combined number of {} descriptors, \
843 accessible across all shader stages, \
844 exceeds the `{}` limit",
845 limit.descriptor_types[1..].iter().fold(
846 format!("`DescriptorType::{:?}`", limit.descriptor_types[0]),
847 |mut s, dt| {
848 write!(s, " + `DescriptorType::{:?}`", dt).unwrap();
849 s
850 }
851 ),
852 limit.limit_name_all,
853 )
854 .into(),
855 vuids: limit.vuids_all,
856 ..Default::default()
857 }));
858 }
859 }
860
861 let mut seen_stages = ShaderStages::empty();
862
863 for (range_index, range) in push_constant_ranges.iter().enumerate() {
864 range
865 .validate(device)
866 .map_err(|err| err.add_context(format!("push_constant_ranges[{}]", range_index)))?;
867
868 let &PushConstantRange {
869 stages,
870 offset: _,
871 size: _,
872 } = range;
873
874 if seen_stages.intersects(stages) {
875 return Err(Box::new(ValidationError {
876 context: "push_constant_ranges".into(),
877 problem: "contains more than one range with the same stage".into(),
878 vuids: &["VUID-VkPipelineLayoutCreateInfo-pPushConstantRanges-00292"],
879 ..Default::default()
880 }));
881 }
882
883 seen_stages |= stages;
884 }
885
886 Ok(())
887 }
888
889 pub(crate) fn to_vk<'a>(
890 &self,
891 fields1_vk: &'a PipelineLayoutCreateInfoFields1Vk,
892 ) -> ash::vk::PipelineLayoutCreateInfo<'a> {
893 let &Self {
894 flags,
895 set_layouts: _,
896 push_constant_ranges: _,
897 _ne: _,
898 } = self;
899 let PipelineLayoutCreateInfoFields1Vk {
900 set_layouts_vk,
901 push_constant_ranges_vk,
902 } = fields1_vk;
903
904 ash::vk::PipelineLayoutCreateInfo::default()
905 .flags(flags.into())
906 .set_layouts(set_layouts_vk)
907 .push_constant_ranges(push_constant_ranges_vk)
908 }
909
910 pub(crate) fn to_vk_fields1(&self) -> PipelineLayoutCreateInfoFields1Vk {
911 let &Self {
912 ref set_layouts,
913 ref push_constant_ranges,
914 ..
915 } = self;
916
917 let set_layouts_vk = set_layouts.iter().map(|l| l.handle()).collect();
918 let push_constant_ranges_vk = push_constant_ranges
919 .iter()
920 .map(|range| range.to_vk())
921 .collect();
922
923 PipelineLayoutCreateInfoFields1Vk {
924 set_layouts_vk,
925 push_constant_ranges_vk,
926 }
927 }
928}
929
930pub(crate) struct PipelineLayoutCreateInfoFields1Vk {
931 pub(crate) set_layouts_vk: SmallVec<[ash::vk::DescriptorSetLayout; 4]>,
932 pub(crate) push_constant_ranges_vk: SmallVec<[ash::vk::PushConstantRange; 4]>,
933}
934
935vulkan_bitflags! {
936 #[non_exhaustive]
937
938 PipelineLayoutCreateFlags = PipelineLayoutCreateFlags(u32);
940
941 }
948
949#[derive(Clone, Copy, Debug, PartialEq, Eq)]
951pub struct PushConstantRange {
952 pub stages: ShaderStages,
956
957 pub offset: u32,
963
964 pub size: u32,
970}
971
972impl Default for PushConstantRange {
973 #[inline]
974 fn default() -> Self {
975 Self {
976 stages: ShaderStages::empty(),
977 offset: 0,
978 size: 0,
979 }
980 }
981}
982
983impl PushConstantRange {
984 pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
985 let &Self {
986 stages,
987 offset,
988 size,
989 } = self;
990
991 stages.validate_device(device).map_err(|err| {
992 err.add_context("stages")
993 .set_vuids(&["VUID-VkPushConstantRange-stageFlags-parameter"])
994 })?;
995
996 if stages.is_empty() {
997 return Err(Box::new(ValidationError {
998 context: "stages".into(),
999 problem: "is empty".into(),
1000 vuids: &["VUID-VkPushConstantRange-stageFlags-requiredbitmask"],
1001 ..Default::default()
1002 }));
1003 }
1004
1005 let max_push_constants_size = device
1006 .physical_device()
1007 .properties()
1008 .max_push_constants_size;
1009
1010 if offset >= max_push_constants_size {
1011 return Err(Box::new(ValidationError {
1012 context: "offset".into(),
1013 problem: "is not less than the `max_push_constants_size` limit".into(),
1014 vuids: &["VUID-VkPushConstantRange-offset-00294"],
1015 ..Default::default()
1016 }));
1017 }
1018
1019 if offset % 4 != 0 {
1020 return Err(Box::new(ValidationError {
1021 context: "offset".into(),
1022 problem: "is not a multiple of 4".into(),
1023 vuids: &["VUID-VkPushConstantRange-offset-00295"],
1024 ..Default::default()
1025 }));
1026 }
1027
1028 if size == 0 {
1029 return Err(Box::new(ValidationError {
1030 context: "size".into(),
1031 problem: "is zero".into(),
1032 vuids: &["VUID-VkPushConstantRange-size-00296"],
1033 ..Default::default()
1034 }));
1035 }
1036
1037 if size % 4 != 0 {
1038 return Err(Box::new(ValidationError {
1039 context: "size".into(),
1040 problem: "is not a multiple of 4".into(),
1041 vuids: &["VUID-VkPushConstantRange-size-00297"],
1042 ..Default::default()
1043 }));
1044 }
1045
1046 if size > max_push_constants_size - offset {
1047 return Err(Box::new(ValidationError {
1048 problem: "`size` is greater than `max_push_constants_size` limit minus `offset`"
1049 .into(),
1050 vuids: &["VUID-VkPushConstantRange-size-00298"],
1051 ..Default::default()
1052 }));
1053 }
1054
1055 Ok(())
1056 }
1057
1058 #[allow(clippy::wrong_self_convention)]
1059 pub(crate) fn to_vk(&self) -> ash::vk::PushConstantRange {
1060 let &Self {
1061 stages,
1062 offset,
1063 size,
1064 } = self;
1065
1066 ash::vk::PushConstantRange {
1067 stage_flags: stages.into(),
1068 offset,
1069 size,
1070 }
1071 }
1072}
1073
1074#[derive(Clone, Debug)]
1077pub struct PipelineDescriptorSetLayoutCreateInfo {
1078 pub flags: PipelineLayoutCreateFlags,
1079 pub set_layouts: Vec<DescriptorSetLayoutCreateInfo>,
1080 pub push_constant_ranges: Vec<PushConstantRange>,
1081}
1082
1083impl PipelineDescriptorSetLayoutCreateInfo {
1084 pub fn from_stages<'a>(
1114 stages: impl IntoIterator<Item = &'a PipelineShaderStageCreateInfo>,
1115 ) -> Self {
1116 let mut descriptor_binding_requirements: HashMap<
1119 (u32, u32),
1120 DescriptorBindingRequirements,
1121 > = HashMap::default();
1122 let mut max_set_num = 0;
1123 let mut push_constant_ranges: Vec<PushConstantRange> = Vec::new();
1124
1125 for stage in stages {
1126 let entry_point_info = stage.entry_point.info();
1127
1128 for (&(set_num, binding_num), reqs) in &entry_point_info.descriptor_binding_requirements
1129 {
1130 max_set_num = max(max_set_num, set_num);
1131
1132 match descriptor_binding_requirements.entry((set_num, binding_num)) {
1133 Entry::Occupied(entry) => {
1134 entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements");
1139 }
1140 Entry::Vacant(entry) => {
1141 entry.insert(reqs.clone());
1144 }
1145 }
1146 }
1147
1148 if let Some(range) = &entry_point_info.push_constant_requirements {
1149 if let Some(existing_range) =
1150 push_constant_ranges.iter_mut().find(|existing_range| {
1151 existing_range.offset == range.offset && existing_range.size == range.size
1152 })
1153 {
1154 existing_range.stages |= range.stages;
1156 } else {
1157 push_constant_ranges.push(*range)
1159 }
1160 }
1161 }
1162
1163 let mut set_layouts =
1165 vec![DescriptorSetLayoutCreateInfo::default(); max_set_num as usize + 1];
1166
1167 for ((set_num, binding_num), reqs) in descriptor_binding_requirements {
1168 set_layouts[set_num as usize]
1169 .bindings
1170 .insert(binding_num, DescriptorSetLayoutBinding::from(&reqs));
1171 }
1172
1173 Self {
1174 flags: PipelineLayoutCreateFlags::empty(),
1175 set_layouts,
1176 push_constant_ranges,
1177 }
1178 }
1179
1180 pub fn into_pipeline_layout_create_info(
1183 self,
1184 device: Arc<Device>,
1185 ) -> Result<PipelineLayoutCreateInfo, IntoPipelineLayoutCreateInfoError> {
1186 let PipelineDescriptorSetLayoutCreateInfo {
1187 flags,
1188 set_layouts,
1189 push_constant_ranges,
1190 } = self;
1191
1192 let set_layouts = set_layouts
1193 .into_iter()
1194 .enumerate()
1195 .map(|(set_num, create_info)| {
1196 DescriptorSetLayout::new(device.clone(), create_info).map_err(|error| {
1197 IntoPipelineLayoutCreateInfoError {
1198 set_num: set_num as u32,
1199 error,
1200 }
1201 })
1202 })
1203 .collect::<Result<Vec<_>, _>>()?;
1204
1205 Ok(PipelineLayoutCreateInfo {
1206 flags,
1207 set_layouts,
1208 push_constant_ranges,
1209 _ne: crate::NonExhaustive(()),
1210 })
1211 }
1212}
1213
1214#[derive(Clone, Debug)]
1215pub struct IntoPipelineLayoutCreateInfoError {
1216 pub set_num: u32,
1217 pub error: Validated<VulkanError>,
1218}
1219
1220impl Display for IntoPipelineLayoutCreateInfoError {
1221 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1222 write!(
1223 f,
1224 "an error occurred while creating a descriptor set layout for set number {}",
1225 self.set_num
1226 )
1227 }
1228}
1229
1230impl Error for IntoPipelineLayoutCreateInfoError {
1231 fn source(&self) -> Option<&(dyn Error + 'static)> {
1232 Some(&self.error)
1233 }
1234}
1235
1236#[cfg(test)]
1237mod tests {
1238
1239 use super::PipelineLayout;
1240 use crate::{
1241 pipeline::layout::{PipelineLayoutCreateInfo, PushConstantRange},
1242 shader::ShaderStages,
1243 };
1244
1245 #[test]
1246 fn push_constant_ranges_disjoint() {
1247 let test_cases = [
1248 (
1256 &[
1257 PushConstantRange {
1258 stages: ShaderStages::FRAGMENT,
1259 offset: 0,
1260 size: 12,
1261 },
1262 PushConstantRange {
1263 stages: ShaderStages::VERTEX,
1264 offset: 0,
1265 size: 40,
1266 },
1267 ][..],
1268 &[
1269 PushConstantRange {
1270 stages: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
1271 offset: 0,
1272 size: 12,
1273 },
1274 PushConstantRange {
1275 stages: ShaderStages::VERTEX,
1276 offset: 12,
1277 size: 28,
1278 },
1279 ][..],
1280 ),
1281 (
1290 &[
1291 PushConstantRange {
1292 stages: ShaderStages::FRAGMENT,
1293 offset: 0,
1294 size: 12,
1295 },
1296 PushConstantRange {
1297 stages: ShaderStages::VERTEX,
1298 offset: 4,
1299 size: 36,
1300 },
1301 ][..],
1302 &[
1303 PushConstantRange {
1304 stages: ShaderStages::FRAGMENT,
1305 offset: 0,
1306 size: 4,
1307 },
1308 PushConstantRange {
1309 stages: ShaderStages::FRAGMENT | ShaderStages::VERTEX,
1310 offset: 4,
1311 size: 8,
1312 },
1313 PushConstantRange {
1314 stages: ShaderStages::VERTEX,
1315 offset: 12,
1316 size: 28,
1317 },
1318 ][..],
1319 ),
1320 (
1333 &[
1334 PushConstantRange {
1335 stages: ShaderStages::FRAGMENT,
1336 offset: 0,
1337 size: 12,
1338 },
1339 PushConstantRange {
1340 stages: ShaderStages::COMPUTE,
1341 offset: 8,
1342 size: 12,
1343 },
1344 PushConstantRange {
1345 stages: ShaderStages::VERTEX,
1346 offset: 4,
1347 size: 12,
1348 },
1349 PushConstantRange {
1350 stages: ShaderStages::TESSELLATION_CONTROL,
1351 offset: 8,
1352 size: 24,
1353 },
1354 ][..],
1355 &[
1356 PushConstantRange {
1357 stages: ShaderStages::FRAGMENT,
1358 offset: 0,
1359 size: 4,
1360 },
1361 PushConstantRange {
1362 stages: ShaderStages::FRAGMENT | ShaderStages::VERTEX,
1363 offset: 4,
1364 size: 4,
1365 },
1366 PushConstantRange {
1367 stages: ShaderStages::VERTEX
1368 | ShaderStages::FRAGMENT
1369 | ShaderStages::COMPUTE
1370 | ShaderStages::TESSELLATION_CONTROL,
1371 offset: 8,
1372 size: 4,
1373 },
1374 PushConstantRange {
1375 stages: ShaderStages::VERTEX
1376 | ShaderStages::COMPUTE
1377 | ShaderStages::TESSELLATION_CONTROL,
1378 offset: 12,
1379 size: 4,
1380 },
1381 PushConstantRange {
1382 stages: ShaderStages::COMPUTE | ShaderStages::TESSELLATION_CONTROL,
1383 offset: 16,
1384 size: 4,
1385 },
1386 PushConstantRange {
1387 stages: ShaderStages::TESSELLATION_CONTROL,
1388 offset: 20,
1389 size: 12,
1390 },
1391 ][..],
1392 ),
1393 ];
1394
1395 let (device, _) = gfx_dev_and_queue!();
1396
1397 for (input, expected) in test_cases {
1398 let layout = PipelineLayout::new(
1399 device.clone(),
1400 PipelineLayoutCreateInfo {
1401 push_constant_ranges: input.into(),
1402 ..Default::default()
1403 },
1404 )
1405 .unwrap();
1406
1407 assert_eq!(layout.push_constant_ranges_disjoint.as_slice(), expected);
1408 }
1409 }
1410}
1411
1412